added options "game.forced_scroll_x" and "game.forced_scroll_y"
[rocksndiamonds.git] / src / game.c
1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // game.c
10 // ============================================================================
11
12 #include "libgame/libgame.h"
13
14 #include "game.h"
15 #include "init.h"
16 #include "tools.h"
17 #include "screens.h"
18 #include "events.h"
19 #include "files.h"
20 #include "tape.h"
21 #include "network.h"
22 #include "anim.h"
23
24
25 // DEBUG SETTINGS
26 #define DEBUG_INIT_PLAYER       1
27 #define DEBUG_PLAYER_ACTIONS    0
28
29 // EXPERIMENTAL STUFF
30 #define USE_NEW_AMOEBA_CODE     FALSE
31
32 // EXPERIMENTAL STUFF
33 #define USE_QUICKSAND_BD_ROCK_BUGFIX    0
34 #define USE_QUICKSAND_IMPACT_BUGFIX     0
35 #define USE_DELAYED_GFX_REDRAW          0
36 #define USE_NEW_PLAYER_ASSIGNMENTS      1
37
38 #if USE_DELAYED_GFX_REDRAW
39 #define TEST_DrawLevelField(x, y)                               \
40         GfxRedraw[x][y] |= GFX_REDRAW_TILE
41 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
42         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
43 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
44         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
45 #define TEST_DrawTwinkleOnField(x, y)                           \
46         GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
47 #else
48 #define TEST_DrawLevelField(x, y)                               \
49              DrawLevelField(x, y)
50 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
51              DrawLevelFieldCrumbled(x, y)
52 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
53              DrawLevelFieldCrumbledNeighbours(x, y)
54 #define TEST_DrawTwinkleOnField(x, y)                           \
55              DrawTwinkleOnField(x, y)
56 #endif
57
58
59 // for DigField()
60 #define DF_NO_PUSH              0
61 #define DF_DIG                  1
62 #define DF_SNAP                 2
63
64 // for MovePlayer()
65 #define MP_NO_ACTION            0
66 #define MP_MOVING               1
67 #define MP_ACTION               2
68 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
69
70 // for ScrollPlayer()
71 #define SCROLL_INIT             0
72 #define SCROLL_GO_ON            1
73
74 // for Bang()/Explode()
75 #define EX_PHASE_START          0
76 #define EX_TYPE_NONE            0
77 #define EX_TYPE_NORMAL          (1 << 0)
78 #define EX_TYPE_CENTER          (1 << 1)
79 #define EX_TYPE_BORDER          (1 << 2)
80 #define EX_TYPE_CROSS           (1 << 3)
81 #define EX_TYPE_DYNA            (1 << 4)
82 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
83
84 #define PANEL_OFF()             (game.panel.active == FALSE)
85 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
86 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
87 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
88
89 // game panel display and control definitions
90 #define GAME_PANEL_LEVEL_NUMBER                 0
91 #define GAME_PANEL_GEMS                         1
92 #define GAME_PANEL_INVENTORY_COUNT              2
93 #define GAME_PANEL_INVENTORY_FIRST_1            3
94 #define GAME_PANEL_INVENTORY_FIRST_2            4
95 #define GAME_PANEL_INVENTORY_FIRST_3            5
96 #define GAME_PANEL_INVENTORY_FIRST_4            6
97 #define GAME_PANEL_INVENTORY_FIRST_5            7
98 #define GAME_PANEL_INVENTORY_FIRST_6            8
99 #define GAME_PANEL_INVENTORY_FIRST_7            9
100 #define GAME_PANEL_INVENTORY_FIRST_8            10
101 #define GAME_PANEL_INVENTORY_LAST_1             11
102 #define GAME_PANEL_INVENTORY_LAST_2             12
103 #define GAME_PANEL_INVENTORY_LAST_3             13
104 #define GAME_PANEL_INVENTORY_LAST_4             14
105 #define GAME_PANEL_INVENTORY_LAST_5             15
106 #define GAME_PANEL_INVENTORY_LAST_6             16
107 #define GAME_PANEL_INVENTORY_LAST_7             17
108 #define GAME_PANEL_INVENTORY_LAST_8             18
109 #define GAME_PANEL_KEY_1                        19
110 #define GAME_PANEL_KEY_2                        20
111 #define GAME_PANEL_KEY_3                        21
112 #define GAME_PANEL_KEY_4                        22
113 #define GAME_PANEL_KEY_5                        23
114 #define GAME_PANEL_KEY_6                        24
115 #define GAME_PANEL_KEY_7                        25
116 #define GAME_PANEL_KEY_8                        26
117 #define GAME_PANEL_KEY_WHITE                    27
118 #define GAME_PANEL_KEY_WHITE_COUNT              28
119 #define GAME_PANEL_SCORE                        29
120 #define GAME_PANEL_HIGHSCORE                    30
121 #define GAME_PANEL_TIME                         31
122 #define GAME_PANEL_TIME_HH                      32
123 #define GAME_PANEL_TIME_MM                      33
124 #define GAME_PANEL_TIME_SS                      34
125 #define GAME_PANEL_TIME_ANIM                    35
126 #define GAME_PANEL_HEALTH                       36
127 #define GAME_PANEL_HEALTH_ANIM                  37
128 #define GAME_PANEL_FRAME                        38
129 #define GAME_PANEL_SHIELD_NORMAL                39
130 #define GAME_PANEL_SHIELD_NORMAL_TIME           40
131 #define GAME_PANEL_SHIELD_DEADLY                41
132 #define GAME_PANEL_SHIELD_DEADLY_TIME           42
133 #define GAME_PANEL_EXIT                         43
134 #define GAME_PANEL_EMC_MAGIC_BALL               44
135 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        45
136 #define GAME_PANEL_LIGHT_SWITCH                 46
137 #define GAME_PANEL_LIGHT_SWITCH_TIME            47
138 #define GAME_PANEL_TIMEGATE_SWITCH              48
139 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         49
140 #define GAME_PANEL_SWITCHGATE_SWITCH            50
141 #define GAME_PANEL_EMC_LENSES                   51
142 #define GAME_PANEL_EMC_LENSES_TIME              52
143 #define GAME_PANEL_EMC_MAGNIFIER                53
144 #define GAME_PANEL_EMC_MAGNIFIER_TIME           54
145 #define GAME_PANEL_BALLOON_SWITCH               55
146 #define GAME_PANEL_DYNABOMB_NUMBER              56
147 #define GAME_PANEL_DYNABOMB_SIZE                57
148 #define GAME_PANEL_DYNABOMB_POWER               58
149 #define GAME_PANEL_PENGUINS                     59
150 #define GAME_PANEL_SOKOBAN_OBJECTS              60
151 #define GAME_PANEL_SOKOBAN_FIELDS               61
152 #define GAME_PANEL_ROBOT_WHEEL                  62
153 #define GAME_PANEL_CONVEYOR_BELT_1              63
154 #define GAME_PANEL_CONVEYOR_BELT_2              64
155 #define GAME_PANEL_CONVEYOR_BELT_3              65
156 #define GAME_PANEL_CONVEYOR_BELT_4              66
157 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       67
158 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       68
159 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       69
160 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       70
161 #define GAME_PANEL_MAGIC_WALL                   71
162 #define GAME_PANEL_MAGIC_WALL_TIME              72
163 #define GAME_PANEL_GRAVITY_STATE                73
164 #define GAME_PANEL_GRAPHIC_1                    74
165 #define GAME_PANEL_GRAPHIC_2                    75
166 #define GAME_PANEL_GRAPHIC_3                    76
167 #define GAME_PANEL_GRAPHIC_4                    77
168 #define GAME_PANEL_GRAPHIC_5                    78
169 #define GAME_PANEL_GRAPHIC_6                    79
170 #define GAME_PANEL_GRAPHIC_7                    80
171 #define GAME_PANEL_GRAPHIC_8                    81
172 #define GAME_PANEL_ELEMENT_1                    82
173 #define GAME_PANEL_ELEMENT_2                    83
174 #define GAME_PANEL_ELEMENT_3                    84
175 #define GAME_PANEL_ELEMENT_4                    85
176 #define GAME_PANEL_ELEMENT_5                    86
177 #define GAME_PANEL_ELEMENT_6                    87
178 #define GAME_PANEL_ELEMENT_7                    88
179 #define GAME_PANEL_ELEMENT_8                    89
180 #define GAME_PANEL_ELEMENT_COUNT_1              90
181 #define GAME_PANEL_ELEMENT_COUNT_2              91
182 #define GAME_PANEL_ELEMENT_COUNT_3              92
183 #define GAME_PANEL_ELEMENT_COUNT_4              93
184 #define GAME_PANEL_ELEMENT_COUNT_5              94
185 #define GAME_PANEL_ELEMENT_COUNT_6              95
186 #define GAME_PANEL_ELEMENT_COUNT_7              96
187 #define GAME_PANEL_ELEMENT_COUNT_8              97
188 #define GAME_PANEL_CE_SCORE_1                   98
189 #define GAME_PANEL_CE_SCORE_2                   99
190 #define GAME_PANEL_CE_SCORE_3                   100
191 #define GAME_PANEL_CE_SCORE_4                   101
192 #define GAME_PANEL_CE_SCORE_5                   102
193 #define GAME_PANEL_CE_SCORE_6                   103
194 #define GAME_PANEL_CE_SCORE_7                   104
195 #define GAME_PANEL_CE_SCORE_8                   105
196 #define GAME_PANEL_CE_SCORE_1_ELEMENT           106
197 #define GAME_PANEL_CE_SCORE_2_ELEMENT           107
198 #define GAME_PANEL_CE_SCORE_3_ELEMENT           108
199 #define GAME_PANEL_CE_SCORE_4_ELEMENT           109
200 #define GAME_PANEL_CE_SCORE_5_ELEMENT           110
201 #define GAME_PANEL_CE_SCORE_6_ELEMENT           111
202 #define GAME_PANEL_CE_SCORE_7_ELEMENT           112
203 #define GAME_PANEL_CE_SCORE_8_ELEMENT           113
204 #define GAME_PANEL_PLAYER_NAME                  114
205 #define GAME_PANEL_LEVEL_NAME                   115
206 #define GAME_PANEL_LEVEL_AUTHOR                 116
207
208 #define NUM_GAME_PANEL_CONTROLS                 117
209
210 struct GamePanelOrderInfo
211 {
212   int nr;
213   int sort_priority;
214 };
215
216 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
217
218 struct GamePanelControlInfo
219 {
220   int nr;
221
222   struct TextPosInfo *pos;
223   int type;
224
225   int graphic, graphic_active;
226
227   int value, last_value;
228   int frame, last_frame;
229   int gfx_frame;
230   int gfx_random;
231 };
232
233 static struct GamePanelControlInfo game_panel_controls[] =
234 {
235   {
236     GAME_PANEL_LEVEL_NUMBER,
237     &game.panel.level_number,
238     TYPE_INTEGER,
239   },
240   {
241     GAME_PANEL_GEMS,
242     &game.panel.gems,
243     TYPE_INTEGER,
244   },
245   {
246     GAME_PANEL_INVENTORY_COUNT,
247     &game.panel.inventory_count,
248     TYPE_INTEGER,
249   },
250   {
251     GAME_PANEL_INVENTORY_FIRST_1,
252     &game.panel.inventory_first[0],
253     TYPE_ELEMENT,
254   },
255   {
256     GAME_PANEL_INVENTORY_FIRST_2,
257     &game.panel.inventory_first[1],
258     TYPE_ELEMENT,
259   },
260   {
261     GAME_PANEL_INVENTORY_FIRST_3,
262     &game.panel.inventory_first[2],
263     TYPE_ELEMENT,
264   },
265   {
266     GAME_PANEL_INVENTORY_FIRST_4,
267     &game.panel.inventory_first[3],
268     TYPE_ELEMENT,
269   },
270   {
271     GAME_PANEL_INVENTORY_FIRST_5,
272     &game.panel.inventory_first[4],
273     TYPE_ELEMENT,
274   },
275   {
276     GAME_PANEL_INVENTORY_FIRST_6,
277     &game.panel.inventory_first[5],
278     TYPE_ELEMENT,
279   },
280   {
281     GAME_PANEL_INVENTORY_FIRST_7,
282     &game.panel.inventory_first[6],
283     TYPE_ELEMENT,
284   },
285   {
286     GAME_PANEL_INVENTORY_FIRST_8,
287     &game.panel.inventory_first[7],
288     TYPE_ELEMENT,
289   },
290   {
291     GAME_PANEL_INVENTORY_LAST_1,
292     &game.panel.inventory_last[0],
293     TYPE_ELEMENT,
294   },
295   {
296     GAME_PANEL_INVENTORY_LAST_2,
297     &game.panel.inventory_last[1],
298     TYPE_ELEMENT,
299   },
300   {
301     GAME_PANEL_INVENTORY_LAST_3,
302     &game.panel.inventory_last[2],
303     TYPE_ELEMENT,
304   },
305   {
306     GAME_PANEL_INVENTORY_LAST_4,
307     &game.panel.inventory_last[3],
308     TYPE_ELEMENT,
309   },
310   {
311     GAME_PANEL_INVENTORY_LAST_5,
312     &game.panel.inventory_last[4],
313     TYPE_ELEMENT,
314   },
315   {
316     GAME_PANEL_INVENTORY_LAST_6,
317     &game.panel.inventory_last[5],
318     TYPE_ELEMENT,
319   },
320   {
321     GAME_PANEL_INVENTORY_LAST_7,
322     &game.panel.inventory_last[6],
323     TYPE_ELEMENT,
324   },
325   {
326     GAME_PANEL_INVENTORY_LAST_8,
327     &game.panel.inventory_last[7],
328     TYPE_ELEMENT,
329   },
330   {
331     GAME_PANEL_KEY_1,
332     &game.panel.key[0],
333     TYPE_ELEMENT,
334   },
335   {
336     GAME_PANEL_KEY_2,
337     &game.panel.key[1],
338     TYPE_ELEMENT,
339   },
340   {
341     GAME_PANEL_KEY_3,
342     &game.panel.key[2],
343     TYPE_ELEMENT,
344   },
345   {
346     GAME_PANEL_KEY_4,
347     &game.panel.key[3],
348     TYPE_ELEMENT,
349   },
350   {
351     GAME_PANEL_KEY_5,
352     &game.panel.key[4],
353     TYPE_ELEMENT,
354   },
355   {
356     GAME_PANEL_KEY_6,
357     &game.panel.key[5],
358     TYPE_ELEMENT,
359   },
360   {
361     GAME_PANEL_KEY_7,
362     &game.panel.key[6],
363     TYPE_ELEMENT,
364   },
365   {
366     GAME_PANEL_KEY_8,
367     &game.panel.key[7],
368     TYPE_ELEMENT,
369   },
370   {
371     GAME_PANEL_KEY_WHITE,
372     &game.panel.key_white,
373     TYPE_ELEMENT,
374   },
375   {
376     GAME_PANEL_KEY_WHITE_COUNT,
377     &game.panel.key_white_count,
378     TYPE_INTEGER,
379   },
380   {
381     GAME_PANEL_SCORE,
382     &game.panel.score,
383     TYPE_INTEGER,
384   },
385   {
386     GAME_PANEL_HIGHSCORE,
387     &game.panel.highscore,
388     TYPE_INTEGER,
389   },
390   {
391     GAME_PANEL_TIME,
392     &game.panel.time,
393     TYPE_INTEGER,
394   },
395   {
396     GAME_PANEL_TIME_HH,
397     &game.panel.time_hh,
398     TYPE_INTEGER,
399   },
400   {
401     GAME_PANEL_TIME_MM,
402     &game.panel.time_mm,
403     TYPE_INTEGER,
404   },
405   {
406     GAME_PANEL_TIME_SS,
407     &game.panel.time_ss,
408     TYPE_INTEGER,
409   },
410   {
411     GAME_PANEL_TIME_ANIM,
412     &game.panel.time_anim,
413     TYPE_GRAPHIC,
414
415     IMG_GFX_GAME_PANEL_TIME_ANIM,
416     IMG_GFX_GAME_PANEL_TIME_ANIM_ACTIVE
417   },
418   {
419     GAME_PANEL_HEALTH,
420     &game.panel.health,
421     TYPE_INTEGER,
422   },
423   {
424     GAME_PANEL_HEALTH_ANIM,
425     &game.panel.health_anim,
426     TYPE_GRAPHIC,
427
428     IMG_GFX_GAME_PANEL_HEALTH_ANIM,
429     IMG_GFX_GAME_PANEL_HEALTH_ANIM_ACTIVE
430   },
431   {
432     GAME_PANEL_FRAME,
433     &game.panel.frame,
434     TYPE_INTEGER,
435   },
436   {
437     GAME_PANEL_SHIELD_NORMAL,
438     &game.panel.shield_normal,
439     TYPE_ELEMENT,
440   },
441   {
442     GAME_PANEL_SHIELD_NORMAL_TIME,
443     &game.panel.shield_normal_time,
444     TYPE_INTEGER,
445   },
446   {
447     GAME_PANEL_SHIELD_DEADLY,
448     &game.panel.shield_deadly,
449     TYPE_ELEMENT,
450   },
451   {
452     GAME_PANEL_SHIELD_DEADLY_TIME,
453     &game.panel.shield_deadly_time,
454     TYPE_INTEGER,
455   },
456   {
457     GAME_PANEL_EXIT,
458     &game.panel.exit,
459     TYPE_ELEMENT,
460   },
461   {
462     GAME_PANEL_EMC_MAGIC_BALL,
463     &game.panel.emc_magic_ball,
464     TYPE_ELEMENT,
465   },
466   {
467     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
468     &game.panel.emc_magic_ball_switch,
469     TYPE_ELEMENT,
470   },
471   {
472     GAME_PANEL_LIGHT_SWITCH,
473     &game.panel.light_switch,
474     TYPE_ELEMENT,
475   },
476   {
477     GAME_PANEL_LIGHT_SWITCH_TIME,
478     &game.panel.light_switch_time,
479     TYPE_INTEGER,
480   },
481   {
482     GAME_PANEL_TIMEGATE_SWITCH,
483     &game.panel.timegate_switch,
484     TYPE_ELEMENT,
485   },
486   {
487     GAME_PANEL_TIMEGATE_SWITCH_TIME,
488     &game.panel.timegate_switch_time,
489     TYPE_INTEGER,
490   },
491   {
492     GAME_PANEL_SWITCHGATE_SWITCH,
493     &game.panel.switchgate_switch,
494     TYPE_ELEMENT,
495   },
496   {
497     GAME_PANEL_EMC_LENSES,
498     &game.panel.emc_lenses,
499     TYPE_ELEMENT,
500   },
501   {
502     GAME_PANEL_EMC_LENSES_TIME,
503     &game.panel.emc_lenses_time,
504     TYPE_INTEGER,
505   },
506   {
507     GAME_PANEL_EMC_MAGNIFIER,
508     &game.panel.emc_magnifier,
509     TYPE_ELEMENT,
510   },
511   {
512     GAME_PANEL_EMC_MAGNIFIER_TIME,
513     &game.panel.emc_magnifier_time,
514     TYPE_INTEGER,
515   },
516   {
517     GAME_PANEL_BALLOON_SWITCH,
518     &game.panel.balloon_switch,
519     TYPE_ELEMENT,
520   },
521   {
522     GAME_PANEL_DYNABOMB_NUMBER,
523     &game.panel.dynabomb_number,
524     TYPE_INTEGER,
525   },
526   {
527     GAME_PANEL_DYNABOMB_SIZE,
528     &game.panel.dynabomb_size,
529     TYPE_INTEGER,
530   },
531   {
532     GAME_PANEL_DYNABOMB_POWER,
533     &game.panel.dynabomb_power,
534     TYPE_ELEMENT,
535   },
536   {
537     GAME_PANEL_PENGUINS,
538     &game.panel.penguins,
539     TYPE_INTEGER,
540   },
541   {
542     GAME_PANEL_SOKOBAN_OBJECTS,
543     &game.panel.sokoban_objects,
544     TYPE_INTEGER,
545   },
546   {
547     GAME_PANEL_SOKOBAN_FIELDS,
548     &game.panel.sokoban_fields,
549     TYPE_INTEGER,
550   },
551   {
552     GAME_PANEL_ROBOT_WHEEL,
553     &game.panel.robot_wheel,
554     TYPE_ELEMENT,
555   },
556   {
557     GAME_PANEL_CONVEYOR_BELT_1,
558     &game.panel.conveyor_belt[0],
559     TYPE_ELEMENT,
560   },
561   {
562     GAME_PANEL_CONVEYOR_BELT_2,
563     &game.panel.conveyor_belt[1],
564     TYPE_ELEMENT,
565   },
566   {
567     GAME_PANEL_CONVEYOR_BELT_3,
568     &game.panel.conveyor_belt[2],
569     TYPE_ELEMENT,
570   },
571   {
572     GAME_PANEL_CONVEYOR_BELT_4,
573     &game.panel.conveyor_belt[3],
574     TYPE_ELEMENT,
575   },
576   {
577     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
578     &game.panel.conveyor_belt_switch[0],
579     TYPE_ELEMENT,
580   },
581   {
582     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
583     &game.panel.conveyor_belt_switch[1],
584     TYPE_ELEMENT,
585   },
586   {
587     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
588     &game.panel.conveyor_belt_switch[2],
589     TYPE_ELEMENT,
590   },
591   {
592     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
593     &game.panel.conveyor_belt_switch[3],
594     TYPE_ELEMENT,
595   },
596   {
597     GAME_PANEL_MAGIC_WALL,
598     &game.panel.magic_wall,
599     TYPE_ELEMENT,
600   },
601   {
602     GAME_PANEL_MAGIC_WALL_TIME,
603     &game.panel.magic_wall_time,
604     TYPE_INTEGER,
605   },
606   {
607     GAME_PANEL_GRAVITY_STATE,
608     &game.panel.gravity_state,
609     TYPE_STRING,
610   },
611   {
612     GAME_PANEL_GRAPHIC_1,
613     &game.panel.graphic[0],
614     TYPE_ELEMENT,
615   },
616   {
617     GAME_PANEL_GRAPHIC_2,
618     &game.panel.graphic[1],
619     TYPE_ELEMENT,
620   },
621   {
622     GAME_PANEL_GRAPHIC_3,
623     &game.panel.graphic[2],
624     TYPE_ELEMENT,
625   },
626   {
627     GAME_PANEL_GRAPHIC_4,
628     &game.panel.graphic[3],
629     TYPE_ELEMENT,
630   },
631   {
632     GAME_PANEL_GRAPHIC_5,
633     &game.panel.graphic[4],
634     TYPE_ELEMENT,
635   },
636   {
637     GAME_PANEL_GRAPHIC_6,
638     &game.panel.graphic[5],
639     TYPE_ELEMENT,
640   },
641   {
642     GAME_PANEL_GRAPHIC_7,
643     &game.panel.graphic[6],
644     TYPE_ELEMENT,
645   },
646   {
647     GAME_PANEL_GRAPHIC_8,
648     &game.panel.graphic[7],
649     TYPE_ELEMENT,
650   },
651   {
652     GAME_PANEL_ELEMENT_1,
653     &game.panel.element[0],
654     TYPE_ELEMENT,
655   },
656   {
657     GAME_PANEL_ELEMENT_2,
658     &game.panel.element[1],
659     TYPE_ELEMENT,
660   },
661   {
662     GAME_PANEL_ELEMENT_3,
663     &game.panel.element[2],
664     TYPE_ELEMENT,
665   },
666   {
667     GAME_PANEL_ELEMENT_4,
668     &game.panel.element[3],
669     TYPE_ELEMENT,
670   },
671   {
672     GAME_PANEL_ELEMENT_5,
673     &game.panel.element[4],
674     TYPE_ELEMENT,
675   },
676   {
677     GAME_PANEL_ELEMENT_6,
678     &game.panel.element[5],
679     TYPE_ELEMENT,
680   },
681   {
682     GAME_PANEL_ELEMENT_7,
683     &game.panel.element[6],
684     TYPE_ELEMENT,
685   },
686   {
687     GAME_PANEL_ELEMENT_8,
688     &game.panel.element[7],
689     TYPE_ELEMENT,
690   },
691   {
692     GAME_PANEL_ELEMENT_COUNT_1,
693     &game.panel.element_count[0],
694     TYPE_INTEGER,
695   },
696   {
697     GAME_PANEL_ELEMENT_COUNT_2,
698     &game.panel.element_count[1],
699     TYPE_INTEGER,
700   },
701   {
702     GAME_PANEL_ELEMENT_COUNT_3,
703     &game.panel.element_count[2],
704     TYPE_INTEGER,
705   },
706   {
707     GAME_PANEL_ELEMENT_COUNT_4,
708     &game.panel.element_count[3],
709     TYPE_INTEGER,
710   },
711   {
712     GAME_PANEL_ELEMENT_COUNT_5,
713     &game.panel.element_count[4],
714     TYPE_INTEGER,
715   },
716   {
717     GAME_PANEL_ELEMENT_COUNT_6,
718     &game.panel.element_count[5],
719     TYPE_INTEGER,
720   },
721   {
722     GAME_PANEL_ELEMENT_COUNT_7,
723     &game.panel.element_count[6],
724     TYPE_INTEGER,
725   },
726   {
727     GAME_PANEL_ELEMENT_COUNT_8,
728     &game.panel.element_count[7],
729     TYPE_INTEGER,
730   },
731   {
732     GAME_PANEL_CE_SCORE_1,
733     &game.panel.ce_score[0],
734     TYPE_INTEGER,
735   },
736   {
737     GAME_PANEL_CE_SCORE_2,
738     &game.panel.ce_score[1],
739     TYPE_INTEGER,
740   },
741   {
742     GAME_PANEL_CE_SCORE_3,
743     &game.panel.ce_score[2],
744     TYPE_INTEGER,
745   },
746   {
747     GAME_PANEL_CE_SCORE_4,
748     &game.panel.ce_score[3],
749     TYPE_INTEGER,
750   },
751   {
752     GAME_PANEL_CE_SCORE_5,
753     &game.panel.ce_score[4],
754     TYPE_INTEGER,
755   },
756   {
757     GAME_PANEL_CE_SCORE_6,
758     &game.panel.ce_score[5],
759     TYPE_INTEGER,
760   },
761   {
762     GAME_PANEL_CE_SCORE_7,
763     &game.panel.ce_score[6],
764     TYPE_INTEGER,
765   },
766   {
767     GAME_PANEL_CE_SCORE_8,
768     &game.panel.ce_score[7],
769     TYPE_INTEGER,
770   },
771   {
772     GAME_PANEL_CE_SCORE_1_ELEMENT,
773     &game.panel.ce_score_element[0],
774     TYPE_ELEMENT,
775   },
776   {
777     GAME_PANEL_CE_SCORE_2_ELEMENT,
778     &game.panel.ce_score_element[1],
779     TYPE_ELEMENT,
780   },
781   {
782     GAME_PANEL_CE_SCORE_3_ELEMENT,
783     &game.panel.ce_score_element[2],
784     TYPE_ELEMENT,
785   },
786   {
787     GAME_PANEL_CE_SCORE_4_ELEMENT,
788     &game.panel.ce_score_element[3],
789     TYPE_ELEMENT,
790   },
791   {
792     GAME_PANEL_CE_SCORE_5_ELEMENT,
793     &game.panel.ce_score_element[4],
794     TYPE_ELEMENT,
795   },
796   {
797     GAME_PANEL_CE_SCORE_6_ELEMENT,
798     &game.panel.ce_score_element[5],
799     TYPE_ELEMENT,
800   },
801   {
802     GAME_PANEL_CE_SCORE_7_ELEMENT,
803     &game.panel.ce_score_element[6],
804     TYPE_ELEMENT,
805   },
806   {
807     GAME_PANEL_CE_SCORE_8_ELEMENT,
808     &game.panel.ce_score_element[7],
809     TYPE_ELEMENT,
810   },
811   {
812     GAME_PANEL_PLAYER_NAME,
813     &game.panel.player_name,
814     TYPE_STRING,
815   },
816   {
817     GAME_PANEL_LEVEL_NAME,
818     &game.panel.level_name,
819     TYPE_STRING,
820   },
821   {
822     GAME_PANEL_LEVEL_AUTHOR,
823     &game.panel.level_author,
824     TYPE_STRING,
825   },
826
827   {
828     -1,
829     NULL,
830     -1,
831   }
832 };
833
834 // values for delayed check of falling and moving elements and for collision
835 #define CHECK_DELAY_MOVING      3
836 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
837 #define CHECK_DELAY_COLLISION   2
838 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
839
840 // values for initial player move delay (initial delay counter value)
841 #define INITIAL_MOVE_DELAY_OFF  -1
842 #define INITIAL_MOVE_DELAY_ON   0
843
844 // values for player movement speed (which is in fact a delay value)
845 #define MOVE_DELAY_MIN_SPEED    32
846 #define MOVE_DELAY_NORMAL_SPEED 8
847 #define MOVE_DELAY_HIGH_SPEED   4
848 #define MOVE_DELAY_MAX_SPEED    1
849
850 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
851 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
852
853 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
854 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
855
856 // values for scroll positions
857 #define SCROLL_POSITION_X(x)    ((x) < SBX_Left  + MIDPOSX ? SBX_Left : \
858                                  (x) > SBX_Right + MIDPOSX ? SBX_Right :\
859                                  (x) - MIDPOSX)
860 #define SCROLL_POSITION_Y(y)    ((y) < SBY_Upper + MIDPOSY ? SBY_Upper :\
861                                  (y) > SBY_Lower + MIDPOSY ? SBY_Lower :\
862                                  (y) - MIDPOSY)
863
864 // values for other actions
865 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
866 #define MOVE_STEPSIZE_MIN       (1)
867 #define MOVE_STEPSIZE_MAX       (TILEX)
868
869 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
870 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
871
872 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
873
874 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
875                                  RND(element_info[e].push_delay_random))
876 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
877                                  RND(element_info[e].drop_delay_random))
878 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
879                                  RND(element_info[e].move_delay_random))
880 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
881                                     (element_info[e].move_delay_random))
882 #define GET_NEW_STEP_DELAY(e)   (   (element_info[e].step_delay_fixed) + \
883                                  RND(element_info[e].step_delay_random))
884 #define GET_MAX_STEP_DELAY(e)   (   (element_info[e].step_delay_fixed) + \
885                                     (element_info[e].step_delay_random))
886 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
887                                  RND(element_info[e].ce_value_random_initial))
888 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
889 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
890                                  RND((c)->delay_random * (c)->delay_frames))
891 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
892                                  RND((c)->delay_random))
893
894
895 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
896          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
897
898 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
899         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
900          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
901          (be) + (e) - EL_SELF)
902
903 #define GET_PLAYER_FROM_BITS(p)                                         \
904         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
905
906 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
907         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
908          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
909          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
910          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
911          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
912          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
913          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
914          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
915          (e))
916
917 #define CAN_GROW_INTO(e)                                                \
918         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
919
920 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
921                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
922                                         (condition)))
923
924 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
925                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
926                                         (CAN_MOVE_INTO_ACID(e) &&       \
927                                          Tile[x][y] == EL_ACID) ||      \
928                                         (condition)))
929
930 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
931                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
932                                         (CAN_MOVE_INTO_ACID(e) &&       \
933                                          Tile[x][y] == EL_ACID) ||      \
934                                         (condition)))
935
936 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
937                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
938                                         (condition) ||                  \
939                                         (CAN_MOVE_INTO_ACID(e) &&       \
940                                          Tile[x][y] == EL_ACID) ||      \
941                                         (DONT_COLLIDE_WITH(e) &&        \
942                                          IS_PLAYER(x, y) &&             \
943                                          !PLAYER_ENEMY_PROTECTED(x, y))))
944
945 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
946         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
947
948 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
949         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
950
951 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
952         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Tile[x][y] == EL_EMC_PLANT)
953
954 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
955         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Tile[x][y]) || \
956                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
957
958 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
959         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
960
961 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
962         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Tile[x][y] == EL_DIAMOND)
963
964 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
965         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_FOOD_DARK_YAMYAM(Tile[x][y]))
966
967 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
968         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Tile[x][y]))
969
970 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
971         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Tile[x][y]))
972
973 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
974         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Tile[x][y] == EL_EXIT_OPEN || \
975                                                  Tile[x][y] == EL_EM_EXIT_OPEN || \
976                                                  Tile[x][y] == EL_STEEL_EXIT_OPEN || \
977                                                  Tile[x][y] == EL_EM_STEEL_EXIT_OPEN || \
978                                                  IS_FOOD_PENGUIN(Tile[x][y])))
979 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
980         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
981
982 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
983         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
984
985 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
986         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
987
988 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
989         (IN_LEV_FIELD(x, y) && (Tile[x][y] == EL_EMC_SPRING_BUMPER ||   \
990                                 Tile[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
991
992 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
993
994 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
995                 (!IS_PLAYER(x, y) &&                                    \
996                  IS_EQUAL_OR_IN_GROUP(Tile[x][y], MOVE_ENTER_EL(e)))
997
998 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
999         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
1000
1001 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
1002 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
1003
1004 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
1005 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
1006 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
1007 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1008
1009 #define MM_HEALTH(x)            (MIN(MAX(0, MAX_HEALTH - (x)), MAX_HEALTH))
1010
1011 // game button identifiers
1012 #define GAME_CTRL_ID_STOP               0
1013 #define GAME_CTRL_ID_PAUSE              1
1014 #define GAME_CTRL_ID_PLAY               2
1015 #define GAME_CTRL_ID_UNDO               3
1016 #define GAME_CTRL_ID_REDO               4
1017 #define GAME_CTRL_ID_SAVE               5
1018 #define GAME_CTRL_ID_PAUSE2             6
1019 #define GAME_CTRL_ID_LOAD               7
1020 #define GAME_CTRL_ID_PANEL_STOP         8
1021 #define GAME_CTRL_ID_PANEL_PAUSE        9
1022 #define GAME_CTRL_ID_PANEL_PLAY         10
1023 #define GAME_CTRL_ID_TOUCH_STOP         11
1024 #define GAME_CTRL_ID_TOUCH_PAUSE        12
1025 #define SOUND_CTRL_ID_MUSIC             13
1026 #define SOUND_CTRL_ID_LOOPS             14
1027 #define SOUND_CTRL_ID_SIMPLE            15
1028 #define SOUND_CTRL_ID_PANEL_MUSIC       16
1029 #define SOUND_CTRL_ID_PANEL_LOOPS       17
1030 #define SOUND_CTRL_ID_PANEL_SIMPLE      18
1031
1032 #define NUM_GAME_BUTTONS                19
1033
1034
1035 // forward declaration for internal use
1036
1037 static void CreateField(int, int, int);
1038
1039 static void ResetGfxAnimation(int, int);
1040
1041 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1042 static void AdvanceFrameAndPlayerCounters(int);
1043
1044 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1045 static boolean MovePlayer(struct PlayerInfo *, int, int);
1046 static void ScrollPlayer(struct PlayerInfo *, int);
1047 static void ScrollScreen(struct PlayerInfo *, int);
1048
1049 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1050 static boolean DigFieldByCE(int, int, int);
1051 static boolean SnapField(struct PlayerInfo *, int, int);
1052 static boolean DropElement(struct PlayerInfo *);
1053
1054 static void InitBeltMovement(void);
1055 static void CloseAllOpenTimegates(void);
1056 static void CheckGravityMovement(struct PlayerInfo *);
1057 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1058 static void KillPlayerUnlessEnemyProtected(int, int);
1059 static void KillPlayerUnlessExplosionProtected(int, int);
1060
1061 static void CheckNextToConditions(int, int);
1062 static void TestIfPlayerNextToCustomElement(int, int);
1063 static void TestIfPlayerTouchesCustomElement(int, int);
1064 static void TestIfElementNextToCustomElement(int, int);
1065 static void TestIfElementTouchesCustomElement(int, int);
1066 static void TestIfElementHitsCustomElement(int, int, int);
1067
1068 static void HandleElementChange(int, int, int);
1069 static void ExecuteCustomElementAction(int, int, int, int);
1070 static boolean ChangeElement(int, int, int, int);
1071
1072 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int, int, int);
1073 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1074         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, -1)
1075 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1076         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1077 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1078         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1079 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1080         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1081 #define CheckTriggeredElementChangeByMouse(x, y, e, ev, s)              \
1082         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1083
1084 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1085 #define CheckElementChange(x, y, e, te, ev)                             \
1086         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1087 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1088         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1089 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1090         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1091 #define CheckElementChangeByMouse(x, y, e, ev, s)                       \
1092         CheckElementChangeExt(x, y, e, EL_UNDEFINED, ev, CH_PLAYER_ANY, s)
1093
1094 static void PlayLevelSound(int, int, int);
1095 static void PlayLevelSoundNearest(int, int, int);
1096 static void PlayLevelSoundAction(int, int, int);
1097 static void PlayLevelSoundElementAction(int, int, int, int);
1098 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1099 static void PlayLevelSoundActionIfLoop(int, int, int);
1100 static void StopLevelSoundActionIfLoop(int, int, int);
1101 static void PlayLevelMusic(void);
1102 static void FadeLevelSoundsAndMusic(void);
1103
1104 static void HandleGameButtons(struct GadgetInfo *);
1105
1106 int AmoebaNeighbourNr(int, int);
1107 void AmoebaToDiamond(int, int);
1108 void ContinueMoving(int, int);
1109 void Bang(int, int);
1110 void InitMovDir(int, int);
1111 void InitAmoebaNr(int, int);
1112 void NewHighScore(int, boolean);
1113
1114 void TestIfGoodThingHitsBadThing(int, int, int);
1115 void TestIfBadThingHitsGoodThing(int, int, int);
1116 void TestIfPlayerTouchesBadThing(int, int);
1117 void TestIfPlayerRunsIntoBadThing(int, int, int);
1118 void TestIfBadThingTouchesPlayer(int, int);
1119 void TestIfBadThingRunsIntoPlayer(int, int, int);
1120 void TestIfFriendTouchesBadThing(int, int);
1121 void TestIfBadThingTouchesFriend(int, int);
1122 void TestIfBadThingTouchesOtherBadThing(int, int);
1123 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1124
1125 void KillPlayer(struct PlayerInfo *);
1126 void BuryPlayer(struct PlayerInfo *);
1127 void RemovePlayer(struct PlayerInfo *);
1128 void ExitPlayer(struct PlayerInfo *);
1129
1130 static int getInvisibleActiveFromInvisibleElement(int);
1131 static int getInvisibleFromInvisibleActiveElement(int);
1132
1133 static void TestFieldAfterSnapping(int, int, int, int, int);
1134
1135 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1136
1137 // for detection of endless loops, caused by custom element programming
1138 // (using maximal playfield width x 10 is just a rough approximation)
1139 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1140
1141 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1142 {                                                                       \
1143   if (recursion_loop_detected)                                          \
1144     return (rc);                                                        \
1145                                                                         \
1146   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1147   {                                                                     \
1148     recursion_loop_detected = TRUE;                                     \
1149     recursion_loop_element = (e);                                       \
1150   }                                                                     \
1151                                                                         \
1152   recursion_loop_depth++;                                               \
1153 }
1154
1155 #define RECURSION_LOOP_DETECTION_END()                                  \
1156 {                                                                       \
1157   recursion_loop_depth--;                                               \
1158 }
1159
1160 static int recursion_loop_depth;
1161 static boolean recursion_loop_detected;
1162 static boolean recursion_loop_element;
1163
1164 static int map_player_action[MAX_PLAYERS];
1165
1166
1167 // ----------------------------------------------------------------------------
1168 // definition of elements that automatically change to other elements after
1169 // a specified time, eventually calling a function when changing
1170 // ----------------------------------------------------------------------------
1171
1172 // forward declaration for changer functions
1173 static void InitBuggyBase(int, int);
1174 static void WarnBuggyBase(int, int);
1175
1176 static void InitTrap(int, int);
1177 static void ActivateTrap(int, int);
1178 static void ChangeActiveTrap(int, int);
1179
1180 static void InitRobotWheel(int, int);
1181 static void RunRobotWheel(int, int);
1182 static void StopRobotWheel(int, int);
1183
1184 static void InitTimegateWheel(int, int);
1185 static void RunTimegateWheel(int, int);
1186
1187 static void InitMagicBallDelay(int, int);
1188 static void ActivateMagicBall(int, int);
1189
1190 struct ChangingElementInfo
1191 {
1192   int element;
1193   int target_element;
1194   int change_delay;
1195   void (*pre_change_function)(int x, int y);
1196   void (*change_function)(int x, int y);
1197   void (*post_change_function)(int x, int y);
1198 };
1199
1200 static struct ChangingElementInfo change_delay_list[] =
1201 {
1202   {
1203     EL_NUT_BREAKING,
1204     EL_EMERALD,
1205     6,
1206     NULL,
1207     NULL,
1208     NULL
1209   },
1210   {
1211     EL_PEARL_BREAKING,
1212     EL_EMPTY,
1213     8,
1214     NULL,
1215     NULL,
1216     NULL
1217   },
1218   {
1219     EL_EXIT_OPENING,
1220     EL_EXIT_OPEN,
1221     29,
1222     NULL,
1223     NULL,
1224     NULL
1225   },
1226   {
1227     EL_EXIT_CLOSING,
1228     EL_EXIT_CLOSED,
1229     29,
1230     NULL,
1231     NULL,
1232     NULL
1233   },
1234   {
1235     EL_STEEL_EXIT_OPENING,
1236     EL_STEEL_EXIT_OPEN,
1237     29,
1238     NULL,
1239     NULL,
1240     NULL
1241   },
1242   {
1243     EL_STEEL_EXIT_CLOSING,
1244     EL_STEEL_EXIT_CLOSED,
1245     29,
1246     NULL,
1247     NULL,
1248     NULL
1249   },
1250   {
1251     EL_EM_EXIT_OPENING,
1252     EL_EM_EXIT_OPEN,
1253     29,
1254     NULL,
1255     NULL,
1256     NULL
1257   },
1258   {
1259     EL_EM_EXIT_CLOSING,
1260     EL_EMPTY,
1261     29,
1262     NULL,
1263     NULL,
1264     NULL
1265   },
1266   {
1267     EL_EM_STEEL_EXIT_OPENING,
1268     EL_EM_STEEL_EXIT_OPEN,
1269     29,
1270     NULL,
1271     NULL,
1272     NULL
1273   },
1274   {
1275     EL_EM_STEEL_EXIT_CLOSING,
1276     EL_STEELWALL,
1277     29,
1278     NULL,
1279     NULL,
1280     NULL
1281   },
1282   {
1283     EL_SP_EXIT_OPENING,
1284     EL_SP_EXIT_OPEN,
1285     29,
1286     NULL,
1287     NULL,
1288     NULL
1289   },
1290   {
1291     EL_SP_EXIT_CLOSING,
1292     EL_SP_EXIT_CLOSED,
1293     29,
1294     NULL,
1295     NULL,
1296     NULL
1297   },
1298   {
1299     EL_SWITCHGATE_OPENING,
1300     EL_SWITCHGATE_OPEN,
1301     29,
1302     NULL,
1303     NULL,
1304     NULL
1305   },
1306   {
1307     EL_SWITCHGATE_CLOSING,
1308     EL_SWITCHGATE_CLOSED,
1309     29,
1310     NULL,
1311     NULL,
1312     NULL
1313   },
1314   {
1315     EL_TIMEGATE_OPENING,
1316     EL_TIMEGATE_OPEN,
1317     29,
1318     NULL,
1319     NULL,
1320     NULL
1321   },
1322   {
1323     EL_TIMEGATE_CLOSING,
1324     EL_TIMEGATE_CLOSED,
1325     29,
1326     NULL,
1327     NULL,
1328     NULL
1329   },
1330
1331   {
1332     EL_ACID_SPLASH_LEFT,
1333     EL_EMPTY,
1334     8,
1335     NULL,
1336     NULL,
1337     NULL
1338   },
1339   {
1340     EL_ACID_SPLASH_RIGHT,
1341     EL_EMPTY,
1342     8,
1343     NULL,
1344     NULL,
1345     NULL
1346   },
1347   {
1348     EL_SP_BUGGY_BASE,
1349     EL_SP_BUGGY_BASE_ACTIVATING,
1350     0,
1351     InitBuggyBase,
1352     NULL,
1353     NULL
1354   },
1355   {
1356     EL_SP_BUGGY_BASE_ACTIVATING,
1357     EL_SP_BUGGY_BASE_ACTIVE,
1358     0,
1359     InitBuggyBase,
1360     NULL,
1361     NULL
1362   },
1363   {
1364     EL_SP_BUGGY_BASE_ACTIVE,
1365     EL_SP_BUGGY_BASE,
1366     0,
1367     InitBuggyBase,
1368     WarnBuggyBase,
1369     NULL
1370   },
1371   {
1372     EL_TRAP,
1373     EL_TRAP_ACTIVE,
1374     0,
1375     InitTrap,
1376     NULL,
1377     ActivateTrap
1378   },
1379   {
1380     EL_TRAP_ACTIVE,
1381     EL_TRAP,
1382     31,
1383     NULL,
1384     ChangeActiveTrap,
1385     NULL
1386   },
1387   {
1388     EL_ROBOT_WHEEL_ACTIVE,
1389     EL_ROBOT_WHEEL,
1390     0,
1391     InitRobotWheel,
1392     RunRobotWheel,
1393     StopRobotWheel
1394   },
1395   {
1396     EL_TIMEGATE_SWITCH_ACTIVE,
1397     EL_TIMEGATE_SWITCH,
1398     0,
1399     InitTimegateWheel,
1400     RunTimegateWheel,
1401     NULL
1402   },
1403   {
1404     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1405     EL_DC_TIMEGATE_SWITCH,
1406     0,
1407     InitTimegateWheel,
1408     RunTimegateWheel,
1409     NULL
1410   },
1411   {
1412     EL_EMC_MAGIC_BALL_ACTIVE,
1413     EL_EMC_MAGIC_BALL_ACTIVE,
1414     0,
1415     InitMagicBallDelay,
1416     NULL,
1417     ActivateMagicBall
1418   },
1419   {
1420     EL_EMC_SPRING_BUMPER_ACTIVE,
1421     EL_EMC_SPRING_BUMPER,
1422     8,
1423     NULL,
1424     NULL,
1425     NULL
1426   },
1427   {
1428     EL_DIAGONAL_SHRINKING,
1429     EL_UNDEFINED,
1430     0,
1431     NULL,
1432     NULL,
1433     NULL
1434   },
1435   {
1436     EL_DIAGONAL_GROWING,
1437     EL_UNDEFINED,
1438     0,
1439     NULL,
1440     NULL,
1441     NULL,
1442   },
1443
1444   {
1445     EL_UNDEFINED,
1446     EL_UNDEFINED,
1447     -1,
1448     NULL,
1449     NULL,
1450     NULL
1451   }
1452 };
1453
1454 struct
1455 {
1456   int element;
1457   int push_delay_fixed, push_delay_random;
1458 }
1459 push_delay_list[] =
1460 {
1461   { EL_SPRING,                  0, 0 },
1462   { EL_BALLOON,                 0, 0 },
1463
1464   { EL_SOKOBAN_OBJECT,          2, 0 },
1465   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1466   { EL_SATELLITE,               2, 0 },
1467   { EL_SP_DISK_YELLOW,          2, 0 },
1468
1469   { EL_UNDEFINED,               0, 0 },
1470 };
1471
1472 struct
1473 {
1474   int element;
1475   int move_stepsize;
1476 }
1477 move_stepsize_list[] =
1478 {
1479   { EL_AMOEBA_DROP,             2 },
1480   { EL_AMOEBA_DROPPING,         2 },
1481   { EL_QUICKSAND_FILLING,       1 },
1482   { EL_QUICKSAND_EMPTYING,      1 },
1483   { EL_QUICKSAND_FAST_FILLING,  2 },
1484   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1485   { EL_MAGIC_WALL_FILLING,      2 },
1486   { EL_MAGIC_WALL_EMPTYING,     2 },
1487   { EL_BD_MAGIC_WALL_FILLING,   2 },
1488   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1489   { EL_DC_MAGIC_WALL_FILLING,   2 },
1490   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1491
1492   { EL_UNDEFINED,               0 },
1493 };
1494
1495 struct
1496 {
1497   int element;
1498   int count;
1499 }
1500 collect_count_list[] =
1501 {
1502   { EL_EMERALD,                 1 },
1503   { EL_BD_DIAMOND,              1 },
1504   { EL_EMERALD_YELLOW,          1 },
1505   { EL_EMERALD_RED,             1 },
1506   { EL_EMERALD_PURPLE,          1 },
1507   { EL_DIAMOND,                 3 },
1508   { EL_SP_INFOTRON,             1 },
1509   { EL_PEARL,                   5 },
1510   { EL_CRYSTAL,                 8 },
1511
1512   { EL_UNDEFINED,               0 },
1513 };
1514
1515 struct
1516 {
1517   int element;
1518   int direction;
1519 }
1520 access_direction_list[] =
1521 {
1522   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1523   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1524   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1525   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1526   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1527   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1528   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1529   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1530   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1531   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1532   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1533
1534   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1535   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1536   { EL_SP_PORT_UP,                                                   MV_DOWN },
1537   { EL_SP_PORT_DOWN,                                         MV_UP           },
1538   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1539   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1540   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1541   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1542   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1543   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1544   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1545   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1546   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1547   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1548   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1549   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1550   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1551   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1552   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1553
1554   { EL_UNDEFINED,                       MV_NONE                              }
1555 };
1556
1557 static struct XY xy_topdown[] =
1558 {
1559   {  0, -1 },
1560   { -1,  0 },
1561   { +1,  0 },
1562   {  0, +1 }
1563 };
1564
1565 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1566
1567 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1568 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1569 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Tile[x][y]) || \
1570                                  IS_JUST_CHANGING(x, y))
1571
1572 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1573
1574 // static variables for playfield scan mode (scanning forward or backward)
1575 static int playfield_scan_start_x = 0;
1576 static int playfield_scan_start_y = 0;
1577 static int playfield_scan_delta_x = 1;
1578 static int playfield_scan_delta_y = 1;
1579
1580 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1581                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1582                                      (y) += playfield_scan_delta_y)     \
1583                                 for ((x) = playfield_scan_start_x;      \
1584                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1585                                      (x) += playfield_scan_delta_x)
1586
1587 #ifdef DEBUG
1588 void DEBUG_SetMaximumDynamite(void)
1589 {
1590   int i;
1591
1592   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1593     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1594       local_player->inventory_element[local_player->inventory_size++] =
1595         EL_DYNAMITE;
1596 }
1597 #endif
1598
1599 static void InitPlayfieldScanModeVars(void)
1600 {
1601   if (game.use_reverse_scan_direction)
1602   {
1603     playfield_scan_start_x = lev_fieldx - 1;
1604     playfield_scan_start_y = lev_fieldy - 1;
1605
1606     playfield_scan_delta_x = -1;
1607     playfield_scan_delta_y = -1;
1608   }
1609   else
1610   {
1611     playfield_scan_start_x = 0;
1612     playfield_scan_start_y = 0;
1613
1614     playfield_scan_delta_x = 1;
1615     playfield_scan_delta_y = 1;
1616   }
1617 }
1618
1619 static void InitPlayfieldScanMode(int mode)
1620 {
1621   game.use_reverse_scan_direction =
1622     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1623
1624   InitPlayfieldScanModeVars();
1625 }
1626
1627 static int get_move_delay_from_stepsize(int move_stepsize)
1628 {
1629   move_stepsize =
1630     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1631
1632   // make sure that stepsize value is always a power of 2
1633   move_stepsize = (1 << log_2(move_stepsize));
1634
1635   return TILEX / move_stepsize;
1636 }
1637
1638 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1639                                boolean init_game)
1640 {
1641   int player_nr = player->index_nr;
1642   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1643   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1644
1645   // do no immediately change move delay -- the player might just be moving
1646   player->move_delay_value_next = move_delay;
1647
1648   // information if player can move must be set separately
1649   player->cannot_move = cannot_move;
1650
1651   if (init_game)
1652   {
1653     player->move_delay       = game.initial_move_delay[player_nr];
1654     player->move_delay_value = game.initial_move_delay_value[player_nr];
1655
1656     player->move_delay_value_next = -1;
1657
1658     player->move_delay_reset_counter = 0;
1659   }
1660 }
1661
1662 void GetPlayerConfig(void)
1663 {
1664   GameFrameDelay = setup.game_frame_delay;
1665
1666   if (!audio.sound_available)
1667     setup.sound_simple = FALSE;
1668
1669   if (!audio.loops_available)
1670     setup.sound_loops = FALSE;
1671
1672   if (!audio.music_available)
1673     setup.sound_music = FALSE;
1674
1675   if (!video.fullscreen_available)
1676     setup.fullscreen = FALSE;
1677
1678   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1679
1680   SetAudioMode(setup.sound);
1681 }
1682
1683 int GetElementFromGroupElement(int element)
1684 {
1685   if (IS_GROUP_ELEMENT(element))
1686   {
1687     struct ElementGroupInfo *group = element_info[element].group;
1688     int last_anim_random_frame = gfx.anim_random_frame;
1689     int element_pos;
1690
1691     if (group->choice_mode == ANIM_RANDOM)
1692       gfx.anim_random_frame = RND(group->num_elements_resolved);
1693
1694     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1695                                     group->choice_mode, 0,
1696                                     group->choice_pos);
1697
1698     if (group->choice_mode == ANIM_RANDOM)
1699       gfx.anim_random_frame = last_anim_random_frame;
1700
1701     group->choice_pos++;
1702
1703     element = group->element_resolved[element_pos];
1704   }
1705
1706   return element;
1707 }
1708
1709 static void IncrementSokobanFieldsNeeded(void)
1710 {
1711   if (level.sb_fields_needed)
1712     game.sokoban_fields_still_needed++;
1713 }
1714
1715 static void IncrementSokobanObjectsNeeded(void)
1716 {
1717   if (level.sb_objects_needed)
1718     game.sokoban_objects_still_needed++;
1719 }
1720
1721 static void DecrementSokobanFieldsNeeded(void)
1722 {
1723   if (game.sokoban_fields_still_needed > 0)
1724     game.sokoban_fields_still_needed--;
1725 }
1726
1727 static void DecrementSokobanObjectsNeeded(void)
1728 {
1729   if (game.sokoban_objects_still_needed > 0)
1730     game.sokoban_objects_still_needed--;
1731 }
1732
1733 static void InitPlayerField(int x, int y, int element, boolean init_game)
1734 {
1735   if (element == EL_SP_MURPHY)
1736   {
1737     if (init_game)
1738     {
1739       if (stored_player[0].present)
1740       {
1741         Tile[x][y] = EL_SP_MURPHY_CLONE;
1742
1743         return;
1744       }
1745       else
1746       {
1747         stored_player[0].initial_element = element;
1748         stored_player[0].use_murphy = TRUE;
1749
1750         if (!level.use_artwork_element[0])
1751           stored_player[0].artwork_element = EL_SP_MURPHY;
1752       }
1753
1754       Tile[x][y] = EL_PLAYER_1;
1755     }
1756   }
1757
1758   if (init_game)
1759   {
1760     struct PlayerInfo *player = &stored_player[Tile[x][y] - EL_PLAYER_1];
1761     int jx = player->jx, jy = player->jy;
1762
1763     player->present = TRUE;
1764
1765     player->block_last_field = (element == EL_SP_MURPHY ?
1766                                 level.sp_block_last_field :
1767                                 level.block_last_field);
1768
1769     // ---------- initialize player's last field block delay ------------------
1770
1771     // always start with reliable default value (no adjustment needed)
1772     player->block_delay_adjustment = 0;
1773
1774     // special case 1: in Supaplex, Murphy blocks last field one more frame
1775     if (player->block_last_field && element == EL_SP_MURPHY)
1776       player->block_delay_adjustment = 1;
1777
1778     // special case 2: in game engines before 3.1.1, blocking was different
1779     if (game.use_block_last_field_bug)
1780       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1781
1782     if (!network.enabled || player->connected_network)
1783     {
1784       player->active = TRUE;
1785
1786       // remove potentially duplicate players
1787       if (IN_LEV_FIELD(jx, jy) && StorePlayer[jx][jy] == Tile[x][y])
1788         StorePlayer[jx][jy] = 0;
1789
1790       StorePlayer[x][y] = Tile[x][y];
1791
1792 #if DEBUG_INIT_PLAYER
1793       Debug("game:init:player", "- player element %d activated",
1794             player->element_nr);
1795       Debug("game:init:player", "  (local player is %d and currently %s)",
1796             local_player->element_nr,
1797             local_player->active ? "active" : "not active");
1798     }
1799 #endif
1800
1801     Tile[x][y] = EL_EMPTY;
1802
1803     player->jx = player->last_jx = x;
1804     player->jy = player->last_jy = y;
1805   }
1806
1807   // always check if player was just killed and should be reanimated
1808   {
1809     int player_nr = GET_PLAYER_NR(element);
1810     struct PlayerInfo *player = &stored_player[player_nr];
1811
1812     if (player->active && player->killed)
1813       player->reanimated = TRUE; // if player was just killed, reanimate him
1814   }
1815 }
1816
1817 static void InitField(int x, int y, boolean init_game)
1818 {
1819   int element = Tile[x][y];
1820
1821   switch (element)
1822   {
1823     case EL_SP_MURPHY:
1824     case EL_PLAYER_1:
1825     case EL_PLAYER_2:
1826     case EL_PLAYER_3:
1827     case EL_PLAYER_4:
1828       InitPlayerField(x, y, element, init_game);
1829       break;
1830
1831     case EL_SOKOBAN_FIELD_PLAYER:
1832       element = Tile[x][y] = EL_PLAYER_1;
1833       InitField(x, y, init_game);
1834
1835       element = Tile[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1836       InitField(x, y, init_game);
1837       break;
1838
1839     case EL_SOKOBAN_FIELD_EMPTY:
1840       IncrementSokobanFieldsNeeded();
1841       break;
1842
1843     case EL_SOKOBAN_OBJECT:
1844       IncrementSokobanObjectsNeeded();
1845       break;
1846
1847     case EL_STONEBLOCK:
1848       if (x < lev_fieldx - 1 && Tile[x + 1][y] == EL_ACID)
1849         Tile[x][y] = EL_ACID_POOL_TOPLEFT;
1850       else if (x > 0 && Tile[x - 1][y] == EL_ACID)
1851         Tile[x][y] = EL_ACID_POOL_TOPRIGHT;
1852       else if (y > 0 && Tile[x][y - 1] == EL_ACID_POOL_TOPLEFT)
1853         Tile[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1854       else if (y > 0 && Tile[x][y - 1] == EL_ACID)
1855         Tile[x][y] = EL_ACID_POOL_BOTTOM;
1856       else if (y > 0 && Tile[x][y - 1] == EL_ACID_POOL_TOPRIGHT)
1857         Tile[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1858       break;
1859
1860     case EL_BUG:
1861     case EL_BUG_RIGHT:
1862     case EL_BUG_UP:
1863     case EL_BUG_LEFT:
1864     case EL_BUG_DOWN:
1865     case EL_SPACESHIP:
1866     case EL_SPACESHIP_RIGHT:
1867     case EL_SPACESHIP_UP:
1868     case EL_SPACESHIP_LEFT:
1869     case EL_SPACESHIP_DOWN:
1870     case EL_BD_BUTTERFLY:
1871     case EL_BD_BUTTERFLY_RIGHT:
1872     case EL_BD_BUTTERFLY_UP:
1873     case EL_BD_BUTTERFLY_LEFT:
1874     case EL_BD_BUTTERFLY_DOWN:
1875     case EL_BD_FIREFLY:
1876     case EL_BD_FIREFLY_RIGHT:
1877     case EL_BD_FIREFLY_UP:
1878     case EL_BD_FIREFLY_LEFT:
1879     case EL_BD_FIREFLY_DOWN:
1880     case EL_PACMAN_RIGHT:
1881     case EL_PACMAN_UP:
1882     case EL_PACMAN_LEFT:
1883     case EL_PACMAN_DOWN:
1884     case EL_YAMYAM:
1885     case EL_YAMYAM_LEFT:
1886     case EL_YAMYAM_RIGHT:
1887     case EL_YAMYAM_UP:
1888     case EL_YAMYAM_DOWN:
1889     case EL_DARK_YAMYAM:
1890     case EL_ROBOT:
1891     case EL_PACMAN:
1892     case EL_SP_SNIKSNAK:
1893     case EL_SP_ELECTRON:
1894     case EL_MOLE:
1895     case EL_MOLE_LEFT:
1896     case EL_MOLE_RIGHT:
1897     case EL_MOLE_UP:
1898     case EL_MOLE_DOWN:
1899     case EL_SPRING_LEFT:
1900     case EL_SPRING_RIGHT:
1901       InitMovDir(x, y);
1902       break;
1903
1904     case EL_AMOEBA_FULL:
1905     case EL_BD_AMOEBA:
1906       InitAmoebaNr(x, y);
1907       break;
1908
1909     case EL_AMOEBA_DROP:
1910       if (y == lev_fieldy - 1)
1911       {
1912         Tile[x][y] = EL_AMOEBA_GROWING;
1913         Store[x][y] = EL_AMOEBA_WET;
1914       }
1915       break;
1916
1917     case EL_DYNAMITE_ACTIVE:
1918     case EL_SP_DISK_RED_ACTIVE:
1919     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1920     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1921     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1922     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1923       MovDelay[x][y] = 96;
1924       break;
1925
1926     case EL_EM_DYNAMITE_ACTIVE:
1927       MovDelay[x][y] = 32;
1928       break;
1929
1930     case EL_LAMP:
1931       game.lights_still_needed++;
1932       break;
1933
1934     case EL_PENGUIN:
1935       game.friends_still_needed++;
1936       break;
1937
1938     case EL_PIG:
1939     case EL_DRAGON:
1940       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1941       break;
1942
1943     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1944     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1945     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1946     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1947     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1948     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1949     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1950     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1951     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1952     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1953     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1954     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1955       if (init_game)
1956       {
1957         int belt_nr = getBeltNrFromBeltSwitchElement(Tile[x][y]);
1958         int belt_dir = getBeltDirFromBeltSwitchElement(Tile[x][y]);
1959         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Tile[x][y]);
1960
1961         if (game.belt_dir_nr[belt_nr] == 3)     // initial value
1962         {
1963           game.belt_dir[belt_nr] = belt_dir;
1964           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1965         }
1966         else    // more than one switch -- set it like the first switch
1967         {
1968           Tile[x][y] = Tile[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1969         }
1970       }
1971       break;
1972
1973     case EL_LIGHT_SWITCH_ACTIVE:
1974       if (init_game)
1975         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1976       break;
1977
1978     case EL_INVISIBLE_STEELWALL:
1979     case EL_INVISIBLE_WALL:
1980     case EL_INVISIBLE_SAND:
1981       if (game.light_time_left > 0 ||
1982           game.lenses_time_left > 0)
1983         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
1984       break;
1985
1986     case EL_EMC_MAGIC_BALL:
1987       if (game.ball_active)
1988         Tile[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1989       break;
1990
1991     case EL_EMC_MAGIC_BALL_SWITCH:
1992       if (game.ball_active)
1993         Tile[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1994       break;
1995
1996     case EL_TRIGGER_PLAYER:
1997     case EL_TRIGGER_ELEMENT:
1998     case EL_TRIGGER_CE_VALUE:
1999     case EL_TRIGGER_CE_SCORE:
2000     case EL_SELF:
2001     case EL_ANY_ELEMENT:
2002     case EL_CURRENT_CE_VALUE:
2003     case EL_CURRENT_CE_SCORE:
2004     case EL_PREV_CE_1:
2005     case EL_PREV_CE_2:
2006     case EL_PREV_CE_3:
2007     case EL_PREV_CE_4:
2008     case EL_PREV_CE_5:
2009     case EL_PREV_CE_6:
2010     case EL_PREV_CE_7:
2011     case EL_PREV_CE_8:
2012     case EL_NEXT_CE_1:
2013     case EL_NEXT_CE_2:
2014     case EL_NEXT_CE_3:
2015     case EL_NEXT_CE_4:
2016     case EL_NEXT_CE_5:
2017     case EL_NEXT_CE_6:
2018     case EL_NEXT_CE_7:
2019     case EL_NEXT_CE_8:
2020       // reference elements should not be used on the playfield
2021       Tile[x][y] = EL_EMPTY;
2022       break;
2023
2024     default:
2025       if (IS_CUSTOM_ELEMENT(element))
2026       {
2027         if (CAN_MOVE(element))
2028           InitMovDir(x, y);
2029
2030         if (!element_info[element].use_last_ce_value || init_game)
2031           CustomValue[x][y] = GET_NEW_CE_VALUE(Tile[x][y]);
2032       }
2033       else if (IS_GROUP_ELEMENT(element))
2034       {
2035         Tile[x][y] = GetElementFromGroupElement(element);
2036
2037         InitField(x, y, init_game);
2038       }
2039       else if (IS_EMPTY_ELEMENT(element))
2040       {
2041         GfxElementEmpty[x][y] = element;
2042         Tile[x][y] = EL_EMPTY;
2043
2044         if (element_info[element].use_gfx_element)
2045           game.use_masked_elements = TRUE;
2046       }
2047
2048       break;
2049   }
2050
2051   if (!init_game)
2052     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2053 }
2054
2055 static void InitField_WithBug1(int x, int y, boolean init_game)
2056 {
2057   InitField(x, y, init_game);
2058
2059   // not needed to call InitMovDir() -- already done by InitField()!
2060   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2061       CAN_MOVE(Tile[x][y]))
2062     InitMovDir(x, y);
2063 }
2064
2065 static void InitField_WithBug2(int x, int y, boolean init_game)
2066 {
2067   int old_element = Tile[x][y];
2068
2069   InitField(x, y, init_game);
2070
2071   // not needed to call InitMovDir() -- already done by InitField()!
2072   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2073       CAN_MOVE(old_element) &&
2074       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2075     InitMovDir(x, y);
2076
2077   /* this case is in fact a combination of not less than three bugs:
2078      first, it calls InitMovDir() for elements that can move, although this is
2079      already done by InitField(); then, it checks the element that was at this
2080      field _before_ the call to InitField() (which can change it); lastly, it
2081      was not called for "mole with direction" elements, which were treated as
2082      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2083   */
2084 }
2085
2086 static int get_key_element_from_nr(int key_nr)
2087 {
2088   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2089                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2090                           EL_EM_KEY_1 : EL_KEY_1);
2091
2092   return key_base_element + key_nr;
2093 }
2094
2095 static int get_next_dropped_element(struct PlayerInfo *player)
2096 {
2097   return (player->inventory_size > 0 ?
2098           player->inventory_element[player->inventory_size - 1] :
2099           player->inventory_infinite_element != EL_UNDEFINED ?
2100           player->inventory_infinite_element :
2101           player->dynabombs_left > 0 ?
2102           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2103           EL_UNDEFINED);
2104 }
2105
2106 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2107 {
2108   // pos >= 0: get element from bottom of the stack;
2109   // pos <  0: get element from top of the stack
2110
2111   if (pos < 0)
2112   {
2113     int min_inventory_size = -pos;
2114     int inventory_pos = player->inventory_size - min_inventory_size;
2115     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2116
2117     return (player->inventory_size >= min_inventory_size ?
2118             player->inventory_element[inventory_pos] :
2119             player->inventory_infinite_element != EL_UNDEFINED ?
2120             player->inventory_infinite_element :
2121             player->dynabombs_left >= min_dynabombs_left ?
2122             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2123             EL_UNDEFINED);
2124   }
2125   else
2126   {
2127     int min_dynabombs_left = pos + 1;
2128     int min_inventory_size = pos + 1 - player->dynabombs_left;
2129     int inventory_pos = pos - player->dynabombs_left;
2130
2131     return (player->inventory_infinite_element != EL_UNDEFINED ?
2132             player->inventory_infinite_element :
2133             player->dynabombs_left >= min_dynabombs_left ?
2134             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2135             player->inventory_size >= min_inventory_size ?
2136             player->inventory_element[inventory_pos] :
2137             EL_UNDEFINED);
2138   }
2139 }
2140
2141 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2142 {
2143   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2144   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2145   int compare_result;
2146
2147   if (gpo1->sort_priority != gpo2->sort_priority)
2148     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2149   else
2150     compare_result = gpo1->nr - gpo2->nr;
2151
2152   return compare_result;
2153 }
2154
2155 int getPlayerInventorySize(int player_nr)
2156 {
2157   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2158     return game_em.ply[player_nr]->dynamite;
2159   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2160     return game_sp.red_disk_count;
2161   else
2162     return stored_player[player_nr].inventory_size;
2163 }
2164
2165 static void InitGameControlValues(void)
2166 {
2167   int i;
2168
2169   for (i = 0; game_panel_controls[i].nr != -1; i++)
2170   {
2171     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2172     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2173     struct TextPosInfo *pos = gpc->pos;
2174     int nr = gpc->nr;
2175     int type = gpc->type;
2176
2177     if (nr != i)
2178     {
2179       Error("'game_panel_controls' structure corrupted at %d", i);
2180
2181       Fail("this should not happen -- please debug");
2182     }
2183
2184     // force update of game controls after initialization
2185     gpc->value = gpc->last_value = -1;
2186     gpc->frame = gpc->last_frame = -1;
2187     gpc->gfx_frame = -1;
2188
2189     // determine panel value width for later calculation of alignment
2190     if (type == TYPE_INTEGER || type == TYPE_STRING)
2191     {
2192       pos->width = pos->size * getFontWidth(pos->font);
2193       pos->height = getFontHeight(pos->font);
2194     }
2195     else if (type == TYPE_ELEMENT)
2196     {
2197       pos->width = pos->size;
2198       pos->height = pos->size;
2199     }
2200
2201     // fill structure for game panel draw order
2202     gpo->nr = gpc->nr;
2203     gpo->sort_priority = pos->sort_priority;
2204   }
2205
2206   // sort game panel controls according to sort_priority and control number
2207   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2208         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2209 }
2210
2211 static void UpdatePlayfieldElementCount(void)
2212 {
2213   boolean use_element_count = FALSE;
2214   int i, j, x, y;
2215
2216   // first check if it is needed at all to calculate playfield element count
2217   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2218     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2219       use_element_count = TRUE;
2220
2221   if (!use_element_count)
2222     return;
2223
2224   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2225     element_info[i].element_count = 0;
2226
2227   SCAN_PLAYFIELD(x, y)
2228   {
2229     element_info[Tile[x][y]].element_count++;
2230   }
2231
2232   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2233     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2234       if (IS_IN_GROUP(j, i))
2235         element_info[EL_GROUP_START + i].element_count +=
2236           element_info[j].element_count;
2237 }
2238
2239 static void UpdateGameControlValues(void)
2240 {
2241   int i, k;
2242   int time = (game.LevelSolved ?
2243               game.LevelSolved_CountingTime :
2244               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2245               game_em.lev->time :
2246               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2247               game_sp.time_played :
2248               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2249               game_mm.energy_left :
2250               game.no_level_time_limit ? TimePlayed : TimeLeft);
2251   int score = (game.LevelSolved ?
2252                game.LevelSolved_CountingScore :
2253                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2254                game_em.lev->score :
2255                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2256                game_sp.score :
2257                level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2258                game_mm.score :
2259                game.score);
2260   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2261               game_em.lev->gems_needed :
2262               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2263               game_sp.infotrons_still_needed :
2264               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2265               game_mm.kettles_still_needed :
2266               game.gems_still_needed);
2267   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2268                      game_em.lev->gems_needed > 0 :
2269                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2270                      game_sp.infotrons_still_needed > 0 :
2271                      level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2272                      game_mm.kettles_still_needed > 0 ||
2273                      game_mm.lights_still_needed > 0 :
2274                      game.gems_still_needed > 0 ||
2275                      game.sokoban_fields_still_needed > 0 ||
2276                      game.sokoban_objects_still_needed > 0 ||
2277                      game.lights_still_needed > 0);
2278   int health = (game.LevelSolved ?
2279                 game.LevelSolved_CountingHealth :
2280                 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2281                 MM_HEALTH(game_mm.laser_overload_value) :
2282                 game.health);
2283   int sync_random_frame = INIT_GFX_RANDOM();    // random, but synchronized
2284
2285   UpdatePlayfieldElementCount();
2286
2287   // update game panel control values
2288
2289   // used instead of "level_nr" (for network games)
2290   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = levelset.level_nr;
2291   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2292
2293   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2294   for (i = 0; i < MAX_NUM_KEYS; i++)
2295     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2296   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2297   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2298
2299   if (game.centered_player_nr == -1)
2300   {
2301     for (i = 0; i < MAX_PLAYERS; i++)
2302     {
2303       // only one player in Supaplex game engine
2304       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2305         break;
2306
2307       for (k = 0; k < MAX_NUM_KEYS; k++)
2308       {
2309         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2310         {
2311           if (game_em.ply[i]->keys & (1 << k))
2312             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2313               get_key_element_from_nr(k);
2314         }
2315         else if (stored_player[i].key[k])
2316           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2317             get_key_element_from_nr(k);
2318       }
2319
2320       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2321         getPlayerInventorySize(i);
2322
2323       if (stored_player[i].num_white_keys > 0)
2324         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2325           EL_DC_KEY_WHITE;
2326
2327       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2328         stored_player[i].num_white_keys;
2329     }
2330   }
2331   else
2332   {
2333     int player_nr = game.centered_player_nr;
2334
2335     for (k = 0; k < MAX_NUM_KEYS; k++)
2336     {
2337       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2338       {
2339         if (game_em.ply[player_nr]->keys & (1 << k))
2340           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2341             get_key_element_from_nr(k);
2342       }
2343       else if (stored_player[player_nr].key[k])
2344         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2345           get_key_element_from_nr(k);
2346     }
2347
2348     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2349       getPlayerInventorySize(player_nr);
2350
2351     if (stored_player[player_nr].num_white_keys > 0)
2352       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2353
2354     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2355       stored_player[player_nr].num_white_keys;
2356   }
2357
2358   // re-arrange keys on game panel, if needed or if defined by style settings
2359   for (i = 0; i < MAX_NUM_KEYS + 1; i++)        // all normal keys + white key
2360   {
2361     int nr = GAME_PANEL_KEY_1 + i;
2362     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2363     struct TextPosInfo *pos = gpc->pos;
2364
2365     // skip check if key is not in the player's inventory
2366     if (gpc->value == EL_EMPTY)
2367       continue;
2368
2369     // check if keys should be arranged on panel from left to right
2370     if (pos->style == STYLE_LEFTMOST_POSITION)
2371     {
2372       // check previous key positions (left from current key)
2373       for (k = 0; k < i; k++)
2374       {
2375         int nr_new = GAME_PANEL_KEY_1 + k;
2376
2377         if (game_panel_controls[nr_new].value == EL_EMPTY)
2378         {
2379           game_panel_controls[nr_new].value = gpc->value;
2380           gpc->value = EL_EMPTY;
2381
2382           break;
2383         }
2384       }
2385     }
2386
2387     // check if "undefined" keys can be placed at some other position
2388     if (pos->x == -1 && pos->y == -1)
2389     {
2390       int nr_new = GAME_PANEL_KEY_1 + i % STD_NUM_KEYS;
2391
2392       // 1st try: display key at the same position as normal or EM keys
2393       if (game_panel_controls[nr_new].value == EL_EMPTY)
2394       {
2395         game_panel_controls[nr_new].value = gpc->value;
2396       }
2397       else
2398       {
2399         // 2nd try: display key at the next free position in the key panel
2400         for (k = 0; k < STD_NUM_KEYS; k++)
2401         {
2402           nr_new = GAME_PANEL_KEY_1 + k;
2403
2404           if (game_panel_controls[nr_new].value == EL_EMPTY)
2405           {
2406             game_panel_controls[nr_new].value = gpc->value;
2407
2408             break;
2409           }
2410         }
2411       }
2412     }
2413   }
2414
2415   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2416   {
2417     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2418       get_inventory_element_from_pos(local_player, i);
2419     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2420       get_inventory_element_from_pos(local_player, -i - 1);
2421   }
2422
2423   game_panel_controls[GAME_PANEL_SCORE].value = score;
2424   game_panel_controls[GAME_PANEL_HIGHSCORE].value = scores.entry[0].score;
2425
2426   game_panel_controls[GAME_PANEL_TIME].value = time;
2427
2428   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2429   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2430   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2431
2432   if (level.time == 0)
2433     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2434   else
2435     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2436
2437   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2438   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2439
2440   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2441
2442   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2443     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2444      EL_EMPTY);
2445   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2446     local_player->shield_normal_time_left;
2447   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2448     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2449      EL_EMPTY);
2450   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2451     local_player->shield_deadly_time_left;
2452
2453   game_panel_controls[GAME_PANEL_EXIT].value =
2454     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2455
2456   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2457     (game.ball_active ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2458   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2459     (game.ball_active ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2460      EL_EMC_MAGIC_BALL_SWITCH);
2461
2462   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2463     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2464   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2465     game.light_time_left;
2466
2467   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2468     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2469   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2470     game.timegate_time_left;
2471
2472   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2473     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2474
2475   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2476     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2477   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2478     game.lenses_time_left;
2479
2480   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2481     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2482   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2483     game.magnify_time_left;
2484
2485   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2486     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2487      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2488      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2489      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2490      EL_BALLOON_SWITCH_NONE);
2491
2492   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2493     local_player->dynabomb_count;
2494   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2495     local_player->dynabomb_size;
2496   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2497     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2498
2499   game_panel_controls[GAME_PANEL_PENGUINS].value =
2500     game.friends_still_needed;
2501
2502   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2503     game.sokoban_objects_still_needed;
2504   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2505     game.sokoban_fields_still_needed;
2506
2507   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2508     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2509
2510   for (i = 0; i < NUM_BELTS; i++)
2511   {
2512     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2513       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2514        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2515     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2516       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2517   }
2518
2519   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2520     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2521   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2522     game.magic_wall_time_left;
2523
2524   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2525     local_player->gravity;
2526
2527   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2528     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2529
2530   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2531     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2532       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2533        game.panel.element[i].id : EL_UNDEFINED);
2534
2535   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2536     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2537       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2538        element_info[game.panel.element_count[i].id].element_count : 0);
2539
2540   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2541     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2542       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2543        element_info[game.panel.ce_score[i].id].collect_score : 0);
2544
2545   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2546     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2547       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2548        element_info[game.panel.ce_score_element[i].id].collect_score :
2549        EL_UNDEFINED);
2550
2551   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2552   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2553   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2554
2555   // update game panel control frames
2556
2557   for (i = 0; game_panel_controls[i].nr != -1; i++)
2558   {
2559     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2560
2561     if (gpc->type == TYPE_ELEMENT)
2562     {
2563       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2564       {
2565         int last_anim_random_frame = gfx.anim_random_frame;
2566         int element = gpc->value;
2567         int graphic = el2panelimg(element);
2568         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2569                                sync_random_frame :
2570                                graphic_info[graphic].anim_global_anim_sync ?
2571                                getGlobalAnimSyncFrame() : INIT_GFX_RANDOM());
2572
2573         if (gpc->value != gpc->last_value)
2574         {
2575           gpc->gfx_frame = 0;
2576           gpc->gfx_random = init_gfx_random;
2577         }
2578         else
2579         {
2580           gpc->gfx_frame++;
2581
2582           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2583               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2584             gpc->gfx_random = init_gfx_random;
2585         }
2586
2587         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2588           gfx.anim_random_frame = gpc->gfx_random;
2589
2590         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2591           gpc->gfx_frame = element_info[element].collect_score;
2592
2593         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2594
2595         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2596           gfx.anim_random_frame = last_anim_random_frame;
2597       }
2598     }
2599     else if (gpc->type == TYPE_GRAPHIC)
2600     {
2601       if (gpc->graphic != IMG_UNDEFINED)
2602       {
2603         int last_anim_random_frame = gfx.anim_random_frame;
2604         int graphic = gpc->graphic;
2605         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2606                                sync_random_frame :
2607                                graphic_info[graphic].anim_global_anim_sync ?
2608                                getGlobalAnimSyncFrame() : INIT_GFX_RANDOM());
2609
2610         if (gpc->value != gpc->last_value)
2611         {
2612           gpc->gfx_frame = 0;
2613           gpc->gfx_random = init_gfx_random;
2614         }
2615         else
2616         {
2617           gpc->gfx_frame++;
2618
2619           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2620               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2621             gpc->gfx_random = init_gfx_random;
2622         }
2623
2624         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2625           gfx.anim_random_frame = gpc->gfx_random;
2626
2627         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2628
2629         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2630           gfx.anim_random_frame = last_anim_random_frame;
2631       }
2632     }
2633   }
2634 }
2635
2636 static void DisplayGameControlValues(void)
2637 {
2638   boolean redraw_panel = FALSE;
2639   int i;
2640
2641   for (i = 0; game_panel_controls[i].nr != -1; i++)
2642   {
2643     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2644
2645     if (PANEL_DEACTIVATED(gpc->pos))
2646       continue;
2647
2648     if (gpc->value == gpc->last_value &&
2649         gpc->frame == gpc->last_frame)
2650       continue;
2651
2652     redraw_panel = TRUE;
2653   }
2654
2655   if (!redraw_panel)
2656     return;
2657
2658   // copy default game door content to main double buffer
2659
2660   // !!! CHECK AGAIN !!!
2661   SetPanelBackground();
2662   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2663   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2664
2665   // redraw game control buttons
2666   RedrawGameButtons();
2667
2668   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2669
2670   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2671   {
2672     int nr = game_panel_order[i].nr;
2673     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2674     struct TextPosInfo *pos = gpc->pos;
2675     int type = gpc->type;
2676     int value = gpc->value;
2677     int frame = gpc->frame;
2678     int size = pos->size;
2679     int font = pos->font;
2680     boolean draw_masked = pos->draw_masked;
2681     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2682
2683     if (PANEL_DEACTIVATED(pos))
2684       continue;
2685
2686     if (pos->class == get_hash_from_key("extra_panel_items") &&
2687         !setup.prefer_extra_panel_items)
2688       continue;
2689
2690     gpc->last_value = value;
2691     gpc->last_frame = frame;
2692
2693     if (type == TYPE_INTEGER)
2694     {
2695       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2696           nr == GAME_PANEL_INVENTORY_COUNT ||
2697           nr == GAME_PANEL_SCORE ||
2698           nr == GAME_PANEL_HIGHSCORE ||
2699           nr == GAME_PANEL_TIME)
2700       {
2701         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2702
2703         if (use_dynamic_size)           // use dynamic number of digits
2704         {
2705           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 :
2706                               nr == GAME_PANEL_INVENTORY_COUNT ||
2707                               nr == GAME_PANEL_TIME ? 1000 : 100000);
2708           int size_add = (nr == GAME_PANEL_LEVEL_NUMBER ||
2709                           nr == GAME_PANEL_INVENTORY_COUNT ||
2710                           nr == GAME_PANEL_TIME ? 1 : 2);
2711           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 :
2712                        nr == GAME_PANEL_INVENTORY_COUNT ||
2713                        nr == GAME_PANEL_TIME ? 3 : 5);
2714           int size2 = size1 + size_add;
2715           int font1 = pos->font;
2716           int font2 = pos->font_alt;
2717
2718           size = (value < value_change ? size1 : size2);
2719           font = (value < value_change ? font1 : font2);
2720         }
2721       }
2722
2723       // correct text size if "digits" is zero or less
2724       if (size <= 0)
2725         size = strlen(int2str(value, size));
2726
2727       // dynamically correct text alignment
2728       pos->width = size * getFontWidth(font);
2729
2730       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2731                   int2str(value, size), font, mask_mode);
2732     }
2733     else if (type == TYPE_ELEMENT)
2734     {
2735       int element, graphic;
2736       Bitmap *src_bitmap;
2737       int src_x, src_y;
2738       int width, height;
2739       int dst_x = PANEL_XPOS(pos);
2740       int dst_y = PANEL_YPOS(pos);
2741
2742       if (value != EL_UNDEFINED && value != EL_EMPTY)
2743       {
2744         element = value;
2745         graphic = el2panelimg(value);
2746
2747 #if 0
2748         Debug("game:DisplayGameControlValues", "%d, '%s' [%d]",
2749               element, EL_NAME(element), size);
2750 #endif
2751
2752         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2753           size = TILESIZE;
2754
2755         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2756                               &src_x, &src_y);
2757
2758         width  = graphic_info[graphic].width  * size / TILESIZE;
2759         height = graphic_info[graphic].height * size / TILESIZE;
2760
2761         if (draw_masked)
2762           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2763                            dst_x, dst_y);
2764         else
2765           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2766                      dst_x, dst_y);
2767       }
2768     }
2769     else if (type == TYPE_GRAPHIC)
2770     {
2771       int graphic        = gpc->graphic;
2772       int graphic_active = gpc->graphic_active;
2773       Bitmap *src_bitmap;
2774       int src_x, src_y;
2775       int width, height;
2776       int dst_x = PANEL_XPOS(pos);
2777       int dst_y = PANEL_YPOS(pos);
2778       boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2779                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2780
2781       if (graphic != IMG_UNDEFINED && !skip)
2782       {
2783         if (pos->style == STYLE_REVERSE)
2784           value = 100 - value;
2785
2786         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2787
2788         if (pos->direction & MV_HORIZONTAL)
2789         {
2790           width  = graphic_info[graphic_active].width * value / 100;
2791           height = graphic_info[graphic_active].height;
2792
2793           if (pos->direction == MV_LEFT)
2794           {
2795             src_x += graphic_info[graphic_active].width - width;
2796             dst_x += graphic_info[graphic_active].width - width;
2797           }
2798         }
2799         else
2800         {
2801           width  = graphic_info[graphic_active].width;
2802           height = graphic_info[graphic_active].height * value / 100;
2803
2804           if (pos->direction == MV_UP)
2805           {
2806             src_y += graphic_info[graphic_active].height - height;
2807             dst_y += graphic_info[graphic_active].height - height;
2808           }
2809         }
2810
2811         if (draw_masked)
2812           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2813                            dst_x, dst_y);
2814         else
2815           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2816                      dst_x, dst_y);
2817
2818         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2819
2820         if (pos->direction & MV_HORIZONTAL)
2821         {
2822           if (pos->direction == MV_RIGHT)
2823           {
2824             src_x += width;
2825             dst_x += width;
2826           }
2827           else
2828           {
2829             dst_x = PANEL_XPOS(pos);
2830           }
2831
2832           width = graphic_info[graphic].width - width;
2833         }
2834         else
2835         {
2836           if (pos->direction == MV_DOWN)
2837           {
2838             src_y += height;
2839             dst_y += height;
2840           }
2841           else
2842           {
2843             dst_y = PANEL_YPOS(pos);
2844           }
2845
2846           height = graphic_info[graphic].height - height;
2847         }
2848
2849         if (draw_masked)
2850           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2851                            dst_x, dst_y);
2852         else
2853           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2854                      dst_x, dst_y);
2855       }
2856     }
2857     else if (type == TYPE_STRING)
2858     {
2859       boolean active = (value != 0);
2860       char *state_normal = "off";
2861       char *state_active = "on";
2862       char *state = (active ? state_active : state_normal);
2863       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2864                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2865                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2866                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2867
2868       if (nr == GAME_PANEL_GRAVITY_STATE)
2869       {
2870         int font1 = pos->font;          // (used for normal state)
2871         int font2 = pos->font_alt;      // (used for active state)
2872
2873         font = (active ? font2 : font1);
2874       }
2875
2876       if (s != NULL)
2877       {
2878         char *s_cut;
2879
2880         if (size <= 0)
2881         {
2882           // don't truncate output if "chars" is zero or less
2883           size = strlen(s);
2884
2885           // dynamically correct text alignment
2886           pos->width = size * getFontWidth(font);
2887         }
2888
2889         s_cut = getStringCopyN(s, size);
2890
2891         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2892                     s_cut, font, mask_mode);
2893
2894         free(s_cut);
2895       }
2896     }
2897
2898     redraw_mask |= REDRAW_DOOR_1;
2899   }
2900
2901   SetGameStatus(GAME_MODE_PLAYING);
2902 }
2903
2904 void UpdateAndDisplayGameControlValues(void)
2905 {
2906   if (tape.deactivate_display)
2907     return;
2908
2909   UpdateGameControlValues();
2910   DisplayGameControlValues();
2911 }
2912
2913 void UpdateGameDoorValues(void)
2914 {
2915   UpdateGameControlValues();
2916 }
2917
2918 void DrawGameDoorValues(void)
2919 {
2920   DisplayGameControlValues();
2921 }
2922
2923
2924 // ============================================================================
2925 // InitGameEngine()
2926 // ----------------------------------------------------------------------------
2927 // initialize game engine due to level / tape version number
2928 // ============================================================================
2929
2930 static void InitGameEngine(void)
2931 {
2932   int i, j, k, l, x, y;
2933
2934   // set game engine from tape file when re-playing, else from level file
2935   game.engine_version = (tape.playing ? tape.engine_version :
2936                          level.game_version);
2937
2938   // set single or multi-player game mode (needed for re-playing tapes)
2939   game.team_mode = setup.team_mode;
2940
2941   if (tape.playing)
2942   {
2943     int num_players = 0;
2944
2945     for (i = 0; i < MAX_PLAYERS; i++)
2946       if (tape.player_participates[i])
2947         num_players++;
2948
2949     // multi-player tapes contain input data for more than one player
2950     game.team_mode = (num_players > 1);
2951   }
2952
2953 #if 0
2954   Debug("game:init:level", "level %d: level.game_version  == %06d", level_nr,
2955         level.game_version);
2956   Debug("game:init:level", "          tape.file_version   == %06d",
2957         tape.file_version);
2958   Debug("game:init:level", "          tape.game_version   == %06d",
2959         tape.game_version);
2960   Debug("game:init:level", "          tape.engine_version == %06d",
2961         tape.engine_version);
2962   Debug("game:init:level", "       => game.engine_version == %06d [tape mode: %s]",
2963         game.engine_version, (tape.playing ? "PLAYING" : "RECORDING"));
2964 #endif
2965
2966   // --------------------------------------------------------------------------
2967   // set flags for bugs and changes according to active game engine version
2968   // --------------------------------------------------------------------------
2969
2970   /*
2971     Summary of bugfix:
2972     Fixed property "can fall" for run-time element "EL_AMOEBA_DROPPING"
2973
2974     Bug was introduced in version:
2975     2.0.1
2976
2977     Bug was fixed in version:
2978     4.2.0.0
2979
2980     Description:
2981     In version 2.0.1, a new run-time element "EL_AMOEBA_DROPPING" was added,
2982     but the property "can fall" was missing, which caused some levels to be
2983     unsolvable. This was fixed in version 4.2.0.0.
2984
2985     Affected levels/tapes:
2986     An example for a tape that was fixed by this bugfix is tape 029 from the
2987     level set "rnd_sam_bateman".
2988     The wrong behaviour will still be used for all levels or tapes that were
2989     created/recorded with it. An example for this is tape 023 from the level
2990     set "rnd_gerhard_haeusler", which was recorded with a buggy game engine.
2991   */
2992
2993   boolean use_amoeba_dropping_cannot_fall_bug =
2994     ((game.engine_version >= VERSION_IDENT(2,0,1,0) &&
2995       game.engine_version <  VERSION_IDENT(4,2,0,0)) ||
2996      (tape.playing &&
2997       tape.game_version >= VERSION_IDENT(2,0,1,0) &&
2998       tape.game_version <  VERSION_IDENT(4,2,0,0)));
2999
3000   /*
3001     Summary of bugfix/change:
3002     Fixed move speed of elements entering or leaving magic wall.
3003
3004     Fixed/changed in version:
3005     2.0.1
3006
3007     Description:
3008     Before 2.0.1, move speed of elements entering or leaving magic wall was
3009     twice as fast as it is now.
3010     Since 2.0.1, this is set to a lower value by using move_stepsize_list[].
3011
3012     Affected levels/tapes:
3013     The first condition is generally needed for all levels/tapes before version
3014     2.0.1, which might use the old behaviour before it was changed; known tapes
3015     that are affected: Tape 014 from the level set "rnd_conor_mancone".
3016     The second condition is an exception from the above case and is needed for
3017     the special case of tapes recorded with game (not engine!) version 2.0.1 or
3018     above, but before it was known that this change would break tapes like the
3019     above and was fixed in 4.2.0.0, so that the changed behaviour was active
3020     although the engine version while recording maybe was before 2.0.1. There
3021     are a lot of tapes that are affected by this exception, like tape 006 from
3022     the level set "rnd_conor_mancone".
3023   */
3024
3025   boolean use_old_move_stepsize_for_magic_wall =
3026     (game.engine_version < VERSION_IDENT(2,0,1,0) &&
3027      !(tape.playing &&
3028        tape.game_version >= VERSION_IDENT(2,0,1,0) &&
3029        tape.game_version <  VERSION_IDENT(4,2,0,0)));
3030
3031   /*
3032     Summary of bugfix/change:
3033     Fixed handling for custom elements that change when pushed by the player.
3034
3035     Fixed/changed in version:
3036     3.1.0
3037
3038     Description:
3039     Before 3.1.0, custom elements that "change when pushing" changed directly
3040     after the player started pushing them (until then handled in "DigField()").
3041     Since 3.1.0, these custom elements are not changed until the "pushing"
3042     move of the element is finished (now handled in "ContinueMoving()").
3043
3044     Affected levels/tapes:
3045     The first condition is generally needed for all levels/tapes before version
3046     3.1.0, which might use the old behaviour before it was changed; known tapes
3047     that are affected are some tapes from the level set "Walpurgis Gardens" by
3048     Jamie Cullen.
3049     The second condition is an exception from the above case and is needed for
3050     the special case of tapes recorded with game (not engine!) version 3.1.0 or
3051     above (including some development versions of 3.1.0), but before it was
3052     known that this change would break tapes like the above and was fixed in
3053     3.1.1, so that the changed behaviour was active although the engine version
3054     while recording maybe was before 3.1.0. There is at least one tape that is
3055     affected by this exception, which is the tape for the one-level set "Bug
3056     Machine" by Juergen Bonhagen.
3057   */
3058
3059   game.use_change_when_pushing_bug =
3060     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3061      !(tape.playing &&
3062        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3063        tape.game_version <  VERSION_IDENT(3,1,1,0)));
3064
3065   /*
3066     Summary of bugfix/change:
3067     Fixed handling for blocking the field the player leaves when moving.
3068
3069     Fixed/changed in version:
3070     3.1.1
3071
3072     Description:
3073     Before 3.1.1, when "block last field when moving" was enabled, the field
3074     the player is leaving when moving was blocked for the time of the move,
3075     and was directly unblocked afterwards. This resulted in the last field
3076     being blocked for exactly one less than the number of frames of one player
3077     move. Additionally, even when blocking was disabled, the last field was
3078     blocked for exactly one frame.
3079     Since 3.1.1, due to changes in player movement handling, the last field
3080     is not blocked at all when blocking is disabled. When blocking is enabled,
3081     the last field is blocked for exactly the number of frames of one player
3082     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3083     last field is blocked for exactly one more than the number of frames of
3084     one player move.
3085
3086     Affected levels/tapes:
3087     (!!! yet to be determined -- probably many !!!)
3088   */
3089
3090   game.use_block_last_field_bug =
3091     (game.engine_version < VERSION_IDENT(3,1,1,0));
3092
3093   /* various special flags and settings for native Emerald Mine game engine */
3094
3095   game_em.use_single_button =
3096     (game.engine_version > VERSION_IDENT(4,0,0,2));
3097
3098   game_em.use_snap_key_bug =
3099     (game.engine_version < VERSION_IDENT(4,0,1,0));
3100
3101   game_em.use_random_bug =
3102     (tape.property_bits & TAPE_PROPERTY_EM_RANDOM_BUG);
3103
3104   boolean use_old_em_engine = (game.engine_version < VERSION_IDENT(4,2,0,0));
3105
3106   game_em.use_old_explosions            = use_old_em_engine;
3107   game_em.use_old_android               = use_old_em_engine;
3108   game_em.use_old_push_elements         = use_old_em_engine;
3109   game_em.use_old_push_into_acid        = use_old_em_engine;
3110
3111   game_em.use_wrap_around               = !use_old_em_engine;
3112
3113   // --------------------------------------------------------------------------
3114
3115   // set maximal allowed number of custom element changes per game frame
3116   game.max_num_changes_per_frame = 1;
3117
3118   // default scan direction: scan playfield from top/left to bottom/right
3119   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3120
3121   // dynamically adjust element properties according to game engine version
3122   InitElementPropertiesEngine(game.engine_version);
3123
3124   // ---------- initialize special element properties -------------------------
3125
3126   // "EL_AMOEBA_DROPPING" missed property "can fall" in older game versions
3127   if (use_amoeba_dropping_cannot_fall_bug)
3128     SET_PROPERTY(EL_AMOEBA_DROPPING, EP_CAN_FALL, FALSE);
3129
3130   // ---------- initialize player's initial move delay ------------------------
3131
3132   // dynamically adjust player properties according to level information
3133   for (i = 0; i < MAX_PLAYERS; i++)
3134     game.initial_move_delay_value[i] =
3135       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3136
3137   // dynamically adjust player properties according to game engine version
3138   for (i = 0; i < MAX_PLAYERS; i++)
3139     game.initial_move_delay[i] =
3140       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3141        game.initial_move_delay_value[i] : 0);
3142
3143   // ---------- initialize player's initial push delay ------------------------
3144
3145   // dynamically adjust player properties according to game engine version
3146   game.initial_push_delay_value =
3147     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3148
3149   // ---------- initialize changing elements ----------------------------------
3150
3151   // initialize changing elements information
3152   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3153   {
3154     struct ElementInfo *ei = &element_info[i];
3155
3156     // this pointer might have been changed in the level editor
3157     ei->change = &ei->change_page[0];
3158
3159     if (!IS_CUSTOM_ELEMENT(i))
3160     {
3161       ei->change->target_element = EL_EMPTY_SPACE;
3162       ei->change->delay_fixed = 0;
3163       ei->change->delay_random = 0;
3164       ei->change->delay_frames = 1;
3165     }
3166
3167     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3168     {
3169       ei->has_change_event[j] = FALSE;
3170
3171       ei->event_page_nr[j] = 0;
3172       ei->event_page[j] = &ei->change_page[0];
3173     }
3174   }
3175
3176   // add changing elements from pre-defined list
3177   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3178   {
3179     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3180     struct ElementInfo *ei = &element_info[ch_delay->element];
3181
3182     ei->change->target_element       = ch_delay->target_element;
3183     ei->change->delay_fixed          = ch_delay->change_delay;
3184
3185     ei->change->pre_change_function  = ch_delay->pre_change_function;
3186     ei->change->change_function      = ch_delay->change_function;
3187     ei->change->post_change_function = ch_delay->post_change_function;
3188
3189     ei->change->can_change = TRUE;
3190     ei->change->can_change_or_has_action = TRUE;
3191
3192     ei->has_change_event[CE_DELAY] = TRUE;
3193
3194     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3195     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3196   }
3197
3198   // ---------- initialize if element can trigger global animations -----------
3199
3200   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3201   {
3202     struct ElementInfo *ei = &element_info[i];
3203
3204     ei->has_anim_event = FALSE;
3205   }
3206
3207   InitGlobalAnimEventsForCustomElements();
3208
3209   // ---------- initialize internal run-time variables ------------------------
3210
3211   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3212   {
3213     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3214
3215     for (j = 0; j < ei->num_change_pages; j++)
3216     {
3217       ei->change_page[j].can_change_or_has_action =
3218         (ei->change_page[j].can_change |
3219          ei->change_page[j].has_action);
3220     }
3221   }
3222
3223   // add change events from custom element configuration
3224   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3225   {
3226     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3227
3228     for (j = 0; j < ei->num_change_pages; j++)
3229     {
3230       if (!ei->change_page[j].can_change_or_has_action)
3231         continue;
3232
3233       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3234       {
3235         // only add event page for the first page found with this event
3236         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3237         {
3238           ei->has_change_event[k] = TRUE;
3239
3240           ei->event_page_nr[k] = j;
3241           ei->event_page[k] = &ei->change_page[j];
3242         }
3243       }
3244     }
3245   }
3246
3247   // ---------- initialize reference elements in change conditions ------------
3248
3249   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3250   {
3251     int element = EL_CUSTOM_START + i;
3252     struct ElementInfo *ei = &element_info[element];
3253
3254     for (j = 0; j < ei->num_change_pages; j++)
3255     {
3256       int trigger_element = ei->change_page[j].initial_trigger_element;
3257
3258       if (trigger_element >= EL_PREV_CE_8 &&
3259           trigger_element <= EL_NEXT_CE_8)
3260         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3261
3262       ei->change_page[j].trigger_element = trigger_element;
3263     }
3264   }
3265
3266   // ---------- initialize run-time trigger player and element ----------------
3267
3268   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3269   {
3270     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3271
3272     for (j = 0; j < ei->num_change_pages; j++)
3273     {
3274       struct ElementChangeInfo *change = &ei->change_page[j];
3275
3276       change->actual_trigger_element = EL_EMPTY;
3277       change->actual_trigger_player = EL_EMPTY;
3278       change->actual_trigger_player_bits = CH_PLAYER_NONE;
3279       change->actual_trigger_side = CH_SIDE_NONE;
3280       change->actual_trigger_ce_value = 0;
3281       change->actual_trigger_ce_score = 0;
3282     }
3283   }
3284
3285   // ---------- initialize trigger events -------------------------------------
3286
3287   // initialize trigger events information
3288   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3289     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3290       trigger_events[i][j] = FALSE;
3291
3292   // add trigger events from element change event properties
3293   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3294   {
3295     struct ElementInfo *ei = &element_info[i];
3296
3297     for (j = 0; j < ei->num_change_pages; j++)
3298     {
3299       struct ElementChangeInfo *change = &ei->change_page[j];
3300
3301       if (!change->can_change_or_has_action)
3302         continue;
3303
3304       if (change->has_event[CE_BY_OTHER_ACTION])
3305       {
3306         int trigger_element = change->trigger_element;
3307
3308         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3309         {
3310           if (change->has_event[k])
3311           {
3312             if (IS_GROUP_ELEMENT(trigger_element))
3313             {
3314               struct ElementGroupInfo *group =
3315                 element_info[trigger_element].group;
3316
3317               for (l = 0; l < group->num_elements_resolved; l++)
3318                 trigger_events[group->element_resolved[l]][k] = TRUE;
3319             }
3320             else if (trigger_element == EL_ANY_ELEMENT)
3321               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3322                 trigger_events[l][k] = TRUE;
3323             else
3324               trigger_events[trigger_element][k] = TRUE;
3325           }
3326         }
3327       }
3328     }
3329   }
3330
3331   // ---------- initialize push delay -----------------------------------------
3332
3333   // initialize push delay values to default
3334   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3335   {
3336     if (!IS_CUSTOM_ELEMENT(i))
3337     {
3338       // set default push delay values (corrected since version 3.0.7-1)
3339       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3340       {
3341         element_info[i].push_delay_fixed = 2;
3342         element_info[i].push_delay_random = 8;
3343       }
3344       else
3345       {
3346         element_info[i].push_delay_fixed = 8;
3347         element_info[i].push_delay_random = 8;
3348       }
3349     }
3350   }
3351
3352   // set push delay value for certain elements from pre-defined list
3353   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3354   {
3355     int e = push_delay_list[i].element;
3356
3357     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3358     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3359   }
3360
3361   // set push delay value for Supaplex elements for newer engine versions
3362   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3363   {
3364     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3365     {
3366       if (IS_SP_ELEMENT(i))
3367       {
3368         // set SP push delay to just enough to push under a falling zonk
3369         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3370
3371         element_info[i].push_delay_fixed  = delay;
3372         element_info[i].push_delay_random = 0;
3373       }
3374     }
3375   }
3376
3377   // ---------- initialize move stepsize --------------------------------------
3378
3379   // initialize move stepsize values to default
3380   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3381     if (!IS_CUSTOM_ELEMENT(i))
3382       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3383
3384   // set move stepsize value for certain elements from pre-defined list
3385   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3386   {
3387     int e = move_stepsize_list[i].element;
3388
3389     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3390
3391     // set move stepsize value for certain elements for older engine versions
3392     if (use_old_move_stepsize_for_magic_wall)
3393     {
3394       if (e == EL_MAGIC_WALL_FILLING ||
3395           e == EL_MAGIC_WALL_EMPTYING ||
3396           e == EL_BD_MAGIC_WALL_FILLING ||
3397           e == EL_BD_MAGIC_WALL_EMPTYING)
3398         element_info[e].move_stepsize *= 2;
3399     }
3400   }
3401
3402   // ---------- initialize collect score --------------------------------------
3403
3404   // initialize collect score values for custom elements from initial value
3405   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3406     if (IS_CUSTOM_ELEMENT(i))
3407       element_info[i].collect_score = element_info[i].collect_score_initial;
3408
3409   // ---------- initialize collect count --------------------------------------
3410
3411   // initialize collect count values for non-custom elements
3412   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3413     if (!IS_CUSTOM_ELEMENT(i))
3414       element_info[i].collect_count_initial = 0;
3415
3416   // add collect count values for all elements from pre-defined list
3417   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3418     element_info[collect_count_list[i].element].collect_count_initial =
3419       collect_count_list[i].count;
3420
3421   // ---------- initialize access direction -----------------------------------
3422
3423   // initialize access direction values to default (access from every side)
3424   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3425     if (!IS_CUSTOM_ELEMENT(i))
3426       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3427
3428   // set access direction value for certain elements from pre-defined list
3429   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3430     element_info[access_direction_list[i].element].access_direction =
3431       access_direction_list[i].direction;
3432
3433   // ---------- initialize explosion content ----------------------------------
3434   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3435   {
3436     if (IS_CUSTOM_ELEMENT(i))
3437       continue;
3438
3439     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3440     {
3441       // (content for EL_YAMYAM set at run-time with game.yamyam_content_nr)
3442
3443       element_info[i].content.e[x][y] =
3444         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3445          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3446          i == EL_PLAYER_3 ? EL_EMERALD :
3447          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3448          i == EL_MOLE ? EL_EMERALD_RED :
3449          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3450          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3451          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3452          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3453          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3454          i == EL_WALL_EMERALD ? EL_EMERALD :
3455          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3456          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3457          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3458          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3459          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3460          i == EL_WALL_PEARL ? EL_PEARL :
3461          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3462          EL_EMPTY);
3463     }
3464   }
3465
3466   // ---------- initialize recursion detection --------------------------------
3467   recursion_loop_depth = 0;
3468   recursion_loop_detected = FALSE;
3469   recursion_loop_element = EL_UNDEFINED;
3470
3471   // ---------- initialize graphics engine ------------------------------------
3472   game.scroll_delay_value =
3473     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3474      level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3475      !setup.forced_scroll_delay           ? 0 :
3476      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3477   if (game.forced_scroll_delay_value == -1)
3478     game.scroll_delay_value =
3479       MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3480
3481   // ---------- initialize game engine snapshots ------------------------------
3482   for (i = 0; i < MAX_PLAYERS; i++)
3483     game.snapshot.last_action[i] = 0;
3484   game.snapshot.changed_action = FALSE;
3485   game.snapshot.collected_item = FALSE;
3486   game.snapshot.mode =
3487     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3488      SNAPSHOT_MODE_EVERY_STEP :
3489      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3490      SNAPSHOT_MODE_EVERY_MOVE :
3491      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3492      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3493   game.snapshot.save_snapshot = FALSE;
3494
3495   // ---------- initialize level time for Supaplex engine ---------------------
3496   // Supaplex levels with time limit currently unsupported -- should be added
3497   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3498     level.time = 0;
3499
3500   // ---------- initialize flags for handling game actions --------------------
3501
3502   // set flags for game actions to default values
3503   game.use_key_actions = TRUE;
3504   game.use_mouse_actions = FALSE;
3505
3506   // when using Mirror Magic game engine, handle mouse events only
3507   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
3508   {
3509     game.use_key_actions = FALSE;
3510     game.use_mouse_actions = TRUE;
3511   }
3512
3513   // check for custom elements with mouse click events
3514   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3515   {
3516     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3517     {
3518       int element = EL_CUSTOM_START + i;
3519
3520       if (HAS_ANY_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
3521           HAS_ANY_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE) ||
3522           HAS_ANY_CHANGE_EVENT(element, CE_MOUSE_CLICKED_ON_X) ||
3523           HAS_ANY_CHANGE_EVENT(element, CE_MOUSE_PRESSED_ON_X))
3524         game.use_mouse_actions = TRUE;
3525     }
3526   }
3527 }
3528
3529 static int get_num_special_action(int element, int action_first,
3530                                   int action_last)
3531 {
3532   int num_special_action = 0;
3533   int i, j;
3534
3535   for (i = action_first; i <= action_last; i++)
3536   {
3537     boolean found = FALSE;
3538
3539     for (j = 0; j < NUM_DIRECTIONS; j++)
3540       if (el_act_dir2img(element, i, j) !=
3541           el_act_dir2img(element, ACTION_DEFAULT, j))
3542         found = TRUE;
3543
3544     if (found)
3545       num_special_action++;
3546     else
3547       break;
3548   }
3549
3550   return num_special_action;
3551 }
3552
3553
3554 // ============================================================================
3555 // InitGame()
3556 // ----------------------------------------------------------------------------
3557 // initialize and start new game
3558 // ============================================================================
3559
3560 #if DEBUG_INIT_PLAYER
3561 static void DebugPrintPlayerStatus(char *message)
3562 {
3563   int i;
3564
3565   if (!options.debug)
3566     return;
3567
3568   Debug("game:init:player", "%s:", message);
3569
3570   for (i = 0; i < MAX_PLAYERS; i++)
3571   {
3572     struct PlayerInfo *player = &stored_player[i];
3573
3574     Debug("game:init:player",
3575           "- player %d: present == %d, connected == %d [%d/%d], active == %d%s",
3576           i + 1,
3577           player->present,
3578           player->connected,
3579           player->connected_locally,
3580           player->connected_network,
3581           player->active,
3582           (local_player == player ? " (local player)" : ""));
3583   }
3584 }
3585 #endif
3586
3587 void InitGame(void)
3588 {
3589   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3590   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3591   int fade_mask = REDRAW_FIELD;
3592   boolean restarting = (game_status == GAME_MODE_PLAYING);
3593   boolean emulate_bd = TRUE;    // unless non-BOULDERDASH elements found
3594   boolean emulate_sp = TRUE;    // unless non-SUPAPLEX    elements found
3595   int initial_move_dir = MV_DOWN;
3596   int i, j, x, y;
3597
3598   // required here to update video display before fading (FIX THIS)
3599   DrawMaskedBorder(REDRAW_DOOR_2);
3600
3601   if (!game.restart_level)
3602     CloseDoor(DOOR_CLOSE_1);
3603
3604   if (restarting)
3605   {
3606     // force fading out global animations displayed during game play
3607     SetGameStatus(GAME_MODE_PSEUDO_RESTARTING);
3608   }
3609   else
3610   {
3611     SetGameStatus(GAME_MODE_PLAYING);
3612   }
3613
3614   if (level_editor_test_game)
3615     FadeSkipNextFadeOut();
3616   else
3617     FadeSetEnterScreen();
3618
3619   if (CheckFadeAll())
3620     fade_mask = REDRAW_ALL;
3621
3622   FadeLevelSoundsAndMusic();
3623
3624   ExpireSoundLoops(TRUE);
3625
3626   FadeOut(fade_mask);
3627
3628   if (restarting)
3629   {
3630     // force restarting global animations displayed during game play
3631     RestartGlobalAnimsByStatus(GAME_MODE_PSEUDO_RESTARTING);
3632
3633     // this is required for "transforming" fade modes like cross-fading
3634     // (else global animations will be stopped, but not restarted here)
3635     SetAnimStatusBeforeFading(GAME_MODE_PSEUDO_RESTARTING);
3636
3637     SetGameStatus(GAME_MODE_PLAYING);
3638   }
3639
3640   if (level_editor_test_game)
3641     FadeSkipNextFadeIn();
3642
3643   // needed if different viewport properties defined for playing
3644   ChangeViewportPropertiesIfNeeded();
3645
3646   ClearField();
3647
3648   DrawCompleteVideoDisplay();
3649
3650   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3651
3652   InitGameEngine();
3653   InitGameControlValues();
3654
3655   if (tape.recording)
3656   {
3657     // initialize tape actions from game when recording tape
3658     tape.use_key_actions   = game.use_key_actions;
3659     tape.use_mouse_actions = game.use_mouse_actions;
3660
3661     // initialize visible playfield size when recording tape (for team mode)
3662     tape.scr_fieldx = SCR_FIELDX;
3663     tape.scr_fieldy = SCR_FIELDY;
3664   }
3665
3666   // don't play tapes over network
3667   network_playing = (network.enabled && !tape.playing);
3668
3669   for (i = 0; i < MAX_PLAYERS; i++)
3670   {
3671     struct PlayerInfo *player = &stored_player[i];
3672
3673     player->index_nr = i;
3674     player->index_bit = (1 << i);
3675     player->element_nr = EL_PLAYER_1 + i;
3676
3677     player->present = FALSE;
3678     player->active = FALSE;
3679     player->mapped = FALSE;
3680
3681     player->killed = FALSE;
3682     player->reanimated = FALSE;
3683     player->buried = FALSE;
3684
3685     player->action = 0;
3686     player->effective_action = 0;
3687     player->programmed_action = 0;
3688     player->snap_action = 0;
3689
3690     player->mouse_action.lx = 0;
3691     player->mouse_action.ly = 0;
3692     player->mouse_action.button = 0;
3693     player->mouse_action.button_hint = 0;
3694
3695     player->effective_mouse_action.lx = 0;
3696     player->effective_mouse_action.ly = 0;
3697     player->effective_mouse_action.button = 0;
3698     player->effective_mouse_action.button_hint = 0;
3699
3700     for (j = 0; j < MAX_NUM_KEYS; j++)
3701       player->key[j] = FALSE;
3702
3703     player->num_white_keys = 0;
3704
3705     player->dynabomb_count = 0;
3706     player->dynabomb_size = 1;
3707     player->dynabombs_left = 0;
3708     player->dynabomb_xl = FALSE;
3709
3710     player->MovDir = initial_move_dir;
3711     player->MovPos = 0;
3712     player->GfxPos = 0;
3713     player->GfxDir = initial_move_dir;
3714     player->GfxAction = ACTION_DEFAULT;
3715     player->Frame = 0;
3716     player->StepFrame = 0;
3717
3718     player->initial_element = player->element_nr;
3719     player->artwork_element =
3720       (level.use_artwork_element[i] ? level.artwork_element[i] :
3721        player->element_nr);
3722     player->use_murphy = FALSE;
3723
3724     player->block_last_field = FALSE;   // initialized in InitPlayerField()
3725     player->block_delay_adjustment = 0; // initialized in InitPlayerField()
3726
3727     player->gravity = level.initial_player_gravity[i];
3728
3729     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3730
3731     player->actual_frame_counter.count = 0;
3732     player->actual_frame_counter.value = 1;
3733
3734     player->step_counter = 0;
3735
3736     player->last_move_dir = initial_move_dir;
3737
3738     player->is_active = FALSE;
3739
3740     player->is_waiting = FALSE;
3741     player->is_moving = FALSE;
3742     player->is_auto_moving = FALSE;
3743     player->is_digging = FALSE;
3744     player->is_snapping = FALSE;
3745     player->is_collecting = FALSE;
3746     player->is_pushing = FALSE;
3747     player->is_switching = FALSE;
3748     player->is_dropping = FALSE;
3749     player->is_dropping_pressed = FALSE;
3750
3751     player->is_bored = FALSE;
3752     player->is_sleeping = FALSE;
3753
3754     player->was_waiting = TRUE;
3755     player->was_moving = FALSE;
3756     player->was_snapping = FALSE;
3757     player->was_dropping = FALSE;
3758
3759     player->force_dropping = FALSE;
3760
3761     player->frame_counter_bored = -1;
3762     player->frame_counter_sleeping = -1;
3763
3764     player->anim_delay_counter = 0;
3765     player->post_delay_counter = 0;
3766
3767     player->dir_waiting = initial_move_dir;
3768     player->action_waiting = ACTION_DEFAULT;
3769     player->last_action_waiting = ACTION_DEFAULT;
3770     player->special_action_bored = ACTION_DEFAULT;
3771     player->special_action_sleeping = ACTION_DEFAULT;
3772
3773     player->switch_x = -1;
3774     player->switch_y = -1;
3775
3776     player->drop_x = -1;
3777     player->drop_y = -1;
3778
3779     player->show_envelope = 0;
3780
3781     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3782
3783     player->push_delay       = -1;      // initialized when pushing starts
3784     player->push_delay_value = game.initial_push_delay_value;
3785
3786     player->drop_delay = 0;
3787     player->drop_pressed_delay = 0;
3788
3789     player->last_jx = -1;
3790     player->last_jy = -1;
3791     player->jx = -1;
3792     player->jy = -1;
3793
3794     player->shield_normal_time_left = 0;
3795     player->shield_deadly_time_left = 0;
3796
3797     player->last_removed_element = EL_UNDEFINED;
3798
3799     player->inventory_infinite_element = EL_UNDEFINED;
3800     player->inventory_size = 0;
3801
3802     if (level.use_initial_inventory[i])
3803     {
3804       for (j = 0; j < level.initial_inventory_size[i]; j++)
3805       {
3806         int element = level.initial_inventory_content[i][j];
3807         int collect_count = element_info[element].collect_count_initial;
3808         int k;
3809
3810         if (!IS_CUSTOM_ELEMENT(element))
3811           collect_count = 1;
3812
3813         if (collect_count == 0)
3814           player->inventory_infinite_element = element;
3815         else
3816           for (k = 0; k < collect_count; k++)
3817             if (player->inventory_size < MAX_INVENTORY_SIZE)
3818               player->inventory_element[player->inventory_size++] = element;
3819       }
3820     }
3821
3822     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3823     SnapField(player, 0, 0);
3824
3825     map_player_action[i] = i;
3826   }
3827
3828   network_player_action_received = FALSE;
3829
3830   // initial null action
3831   if (network_playing)
3832     SendToServer_MovePlayer(MV_NONE);
3833
3834   FrameCounter = 0;
3835   TimeFrames = 0;
3836   TimePlayed = 0;
3837   TimeLeft = level.time;
3838   TapeTime = 0;
3839
3840   ScreenMovDir = MV_NONE;
3841   ScreenMovPos = 0;
3842   ScreenGfxPos = 0;
3843
3844   ScrollStepSize = 0;   // will be correctly initialized by ScrollScreen()
3845
3846   game.robot_wheel_x = -1;
3847   game.robot_wheel_y = -1;
3848
3849   game.exit_x = -1;
3850   game.exit_y = -1;
3851
3852   game.all_players_gone = FALSE;
3853
3854   game.LevelSolved = FALSE;
3855   game.GameOver = FALSE;
3856
3857   game.GamePlayed = !tape.playing;
3858
3859   game.LevelSolved_GameWon = FALSE;
3860   game.LevelSolved_GameEnd = FALSE;
3861   game.LevelSolved_SaveTape = FALSE;
3862   game.LevelSolved_SaveScore = FALSE;
3863
3864   game.LevelSolved_CountingTime = 0;
3865   game.LevelSolved_CountingScore = 0;
3866   game.LevelSolved_CountingHealth = 0;
3867
3868   game.panel.active = TRUE;
3869
3870   game.no_level_time_limit = (level.time == 0);
3871   game.time_limit = (leveldir_current->time_limit && setup.time_limit);
3872
3873   game.yamyam_content_nr = 0;
3874   game.robot_wheel_active = FALSE;
3875   game.magic_wall_active = FALSE;
3876   game.magic_wall_time_left = 0;
3877   game.light_time_left = 0;
3878   game.timegate_time_left = 0;
3879   game.switchgate_pos = 0;
3880   game.wind_direction = level.wind_direction_initial;
3881
3882   game.time_final = 0;
3883   game.score_time_final = 0;
3884
3885   game.score = 0;
3886   game.score_final = 0;
3887
3888   game.health = MAX_HEALTH;
3889   game.health_final = MAX_HEALTH;
3890
3891   game.gems_still_needed = level.gems_needed;
3892   game.sokoban_fields_still_needed = 0;
3893   game.sokoban_objects_still_needed = 0;
3894   game.lights_still_needed = 0;
3895   game.players_still_needed = 0;
3896   game.friends_still_needed = 0;
3897
3898   game.lenses_time_left = 0;
3899   game.magnify_time_left = 0;
3900
3901   game.ball_active = level.ball_active_initial;
3902   game.ball_content_nr = 0;
3903
3904   game.explosions_delayed = TRUE;
3905
3906   game.envelope_active = FALSE;
3907
3908   // special case: set custom artwork setting to initial value
3909   game.use_masked_elements = game.use_masked_elements_initial;
3910
3911   for (i = 0; i < NUM_BELTS; i++)
3912   {
3913     game.belt_dir[i] = MV_NONE;
3914     game.belt_dir_nr[i] = 3;            // not moving, next moving left
3915   }
3916
3917   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3918     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3919
3920 #if DEBUG_INIT_PLAYER
3921   DebugPrintPlayerStatus("Player status at level initialization");
3922 #endif
3923
3924   SCAN_PLAYFIELD(x, y)
3925   {
3926     Tile[x][y] = Last[x][y] = level.field[x][y];
3927     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3928     ChangeDelay[x][y] = 0;
3929     ChangePage[x][y] = -1;
3930     CustomValue[x][y] = 0;              // initialized in InitField()
3931     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3932     AmoebaNr[x][y] = 0;
3933     WasJustMoving[x][y] = 0;
3934     WasJustFalling[x][y] = 0;
3935     CheckCollision[x][y] = 0;
3936     CheckImpact[x][y] = 0;
3937     Stop[x][y] = FALSE;
3938     Pushed[x][y] = FALSE;
3939
3940     ChangeCount[x][y] = 0;
3941     ChangeEvent[x][y] = -1;
3942
3943     ExplodePhase[x][y] = 0;
3944     ExplodeDelay[x][y] = 0;
3945     ExplodeField[x][y] = EX_TYPE_NONE;
3946
3947     RunnerVisit[x][y] = 0;
3948     PlayerVisit[x][y] = 0;
3949
3950     GfxFrame[x][y] = 0;
3951     GfxRandom[x][y] = INIT_GFX_RANDOM();
3952     GfxRandomStatic[x][y] = INIT_GFX_RANDOM();
3953     GfxElement[x][y] = EL_UNDEFINED;
3954     GfxElementEmpty[x][y] = EL_EMPTY;
3955     GfxAction[x][y] = ACTION_DEFAULT;
3956     GfxDir[x][y] = MV_NONE;
3957     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3958   }
3959
3960   SCAN_PLAYFIELD(x, y)
3961   {
3962     if (emulate_bd && !IS_BD_ELEMENT(Tile[x][y]))
3963       emulate_bd = FALSE;
3964     if (emulate_sp && !IS_SP_ELEMENT(Tile[x][y]))
3965       emulate_sp = FALSE;
3966
3967     InitField(x, y, TRUE);
3968
3969     ResetGfxAnimation(x, y);
3970   }
3971
3972   InitBeltMovement();
3973
3974   for (i = 0; i < MAX_PLAYERS; i++)
3975   {
3976     struct PlayerInfo *player = &stored_player[i];
3977
3978     // set number of special actions for bored and sleeping animation
3979     player->num_special_action_bored =
3980       get_num_special_action(player->artwork_element,
3981                              ACTION_BORING_1, ACTION_BORING_LAST);
3982     player->num_special_action_sleeping =
3983       get_num_special_action(player->artwork_element,
3984                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3985   }
3986
3987   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3988                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3989
3990   // initialize type of slippery elements
3991   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3992   {
3993     if (!IS_CUSTOM_ELEMENT(i))
3994     {
3995       // default: elements slip down either to the left or right randomly
3996       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3997
3998       // SP style elements prefer to slip down on the left side
3999       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
4000         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
4001
4002       // BD style elements prefer to slip down on the left side
4003       if (game.emulation == EMU_BOULDERDASH)
4004         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
4005     }
4006   }
4007
4008   // initialize explosion and ignition delay
4009   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4010   {
4011     if (!IS_CUSTOM_ELEMENT(i))
4012     {
4013       int num_phase = 8;
4014       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
4015                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
4016                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
4017       int last_phase = (num_phase + 1) * delay;
4018       int half_phase = (num_phase / 2) * delay;
4019
4020       element_info[i].explosion_delay = last_phase - 1;
4021       element_info[i].ignition_delay = half_phase;
4022
4023       if (i == EL_BLACK_ORB)
4024         element_info[i].ignition_delay = 1;
4025     }
4026   }
4027
4028   // correct non-moving belts to start moving left
4029   for (i = 0; i < NUM_BELTS; i++)
4030     if (game.belt_dir[i] == MV_NONE)
4031       game.belt_dir_nr[i] = 3;          // not moving, next moving left
4032
4033 #if USE_NEW_PLAYER_ASSIGNMENTS
4034   // use preferred player also in local single-player mode
4035   if (!network.enabled && !game.team_mode)
4036   {
4037     int new_index_nr = setup.network_player_nr;
4038
4039     if (new_index_nr >= 0 && new_index_nr < MAX_PLAYERS)
4040     {
4041       for (i = 0; i < MAX_PLAYERS; i++)
4042         stored_player[i].connected_locally = FALSE;
4043
4044       stored_player[new_index_nr].connected_locally = TRUE;
4045     }
4046   }
4047
4048   for (i = 0; i < MAX_PLAYERS; i++)
4049   {
4050     stored_player[i].connected = FALSE;
4051
4052     // in network game mode, the local player might not be the first player
4053     if (stored_player[i].connected_locally)
4054       local_player = &stored_player[i];
4055   }
4056
4057   if (!network.enabled)
4058     local_player->connected = TRUE;
4059
4060   if (tape.playing)
4061   {
4062     for (i = 0; i < MAX_PLAYERS; i++)
4063       stored_player[i].connected = tape.player_participates[i];
4064   }
4065   else if (network.enabled)
4066   {
4067     // add team mode players connected over the network (needed for correct
4068     // assignment of player figures from level to locally playing players)
4069
4070     for (i = 0; i < MAX_PLAYERS; i++)
4071       if (stored_player[i].connected_network)
4072         stored_player[i].connected = TRUE;
4073   }
4074   else if (game.team_mode)
4075   {
4076     // try to guess locally connected team mode players (needed for correct
4077     // assignment of player figures from level to locally playing players)
4078
4079     for (i = 0; i < MAX_PLAYERS; i++)
4080       if (setup.input[i].use_joystick ||
4081           setup.input[i].key.left != KSYM_UNDEFINED)
4082         stored_player[i].connected = TRUE;
4083   }
4084
4085 #if DEBUG_INIT_PLAYER
4086   DebugPrintPlayerStatus("Player status after level initialization");
4087 #endif
4088
4089 #if DEBUG_INIT_PLAYER
4090   Debug("game:init:player", "Reassigning players ...");
4091 #endif
4092
4093   // check if any connected player was not found in playfield
4094   for (i = 0; i < MAX_PLAYERS; i++)
4095   {
4096     struct PlayerInfo *player = &stored_player[i];
4097
4098     if (player->connected && !player->present)
4099     {
4100       struct PlayerInfo *field_player = NULL;
4101
4102 #if DEBUG_INIT_PLAYER
4103       Debug("game:init:player",
4104             "- looking for field player for player %d ...", i + 1);
4105 #endif
4106
4107       // assign first free player found that is present in the playfield
4108
4109       // first try: look for unmapped playfield player that is not connected
4110       for (j = 0; j < MAX_PLAYERS; j++)
4111         if (field_player == NULL &&
4112             stored_player[j].present &&
4113             !stored_player[j].mapped &&
4114             !stored_player[j].connected)
4115           field_player = &stored_player[j];
4116
4117       // second try: look for *any* unmapped playfield player
4118       for (j = 0; j < MAX_PLAYERS; j++)
4119         if (field_player == NULL &&
4120             stored_player[j].present &&
4121             !stored_player[j].mapped)
4122           field_player = &stored_player[j];
4123
4124       if (field_player != NULL)
4125       {
4126         int jx = field_player->jx, jy = field_player->jy;
4127
4128 #if DEBUG_INIT_PLAYER
4129         Debug("game:init:player", "- found player %d",
4130               field_player->index_nr + 1);
4131 #endif
4132
4133         player->present = FALSE;
4134         player->active = FALSE;
4135
4136         field_player->present = TRUE;
4137         field_player->active = TRUE;
4138
4139         /*
4140         player->initial_element = field_player->initial_element;
4141         player->artwork_element = field_player->artwork_element;
4142
4143         player->block_last_field       = field_player->block_last_field;
4144         player->block_delay_adjustment = field_player->block_delay_adjustment;
4145         */
4146
4147         StorePlayer[jx][jy] = field_player->element_nr;
4148
4149         field_player->jx = field_player->last_jx = jx;
4150         field_player->jy = field_player->last_jy = jy;
4151
4152         if (local_player == player)
4153           local_player = field_player;
4154
4155         map_player_action[field_player->index_nr] = i;
4156
4157         field_player->mapped = TRUE;
4158
4159 #if DEBUG_INIT_PLAYER
4160         Debug("game:init:player", "- map_player_action[%d] == %d",
4161               field_player->index_nr + 1, i + 1);
4162 #endif
4163       }
4164     }
4165
4166     if (player->connected && player->present)
4167       player->mapped = TRUE;
4168   }
4169
4170 #if DEBUG_INIT_PLAYER
4171   DebugPrintPlayerStatus("Player status after player assignment (first stage)");
4172 #endif
4173
4174 #else
4175
4176   // check if any connected player was not found in playfield
4177   for (i = 0; i < MAX_PLAYERS; i++)
4178   {
4179     struct PlayerInfo *player = &stored_player[i];
4180
4181     if (player->connected && !player->present)
4182     {
4183       for (j = 0; j < MAX_PLAYERS; j++)
4184       {
4185         struct PlayerInfo *field_player = &stored_player[j];
4186         int jx = field_player->jx, jy = field_player->jy;
4187
4188         // assign first free player found that is present in the playfield
4189         if (field_player->present && !field_player->connected)
4190         {
4191           player->present = TRUE;
4192           player->active = TRUE;
4193
4194           field_player->present = FALSE;
4195           field_player->active = FALSE;
4196
4197           player->initial_element = field_player->initial_element;
4198           player->artwork_element = field_player->artwork_element;
4199
4200           player->block_last_field       = field_player->block_last_field;
4201           player->block_delay_adjustment = field_player->block_delay_adjustment;
4202
4203           StorePlayer[jx][jy] = player->element_nr;
4204
4205           player->jx = player->last_jx = jx;
4206           player->jy = player->last_jy = jy;
4207
4208           break;
4209         }
4210       }
4211     }
4212   }
4213 #endif
4214
4215 #if 0
4216   Debug("game:init:player", "local_player->present == %d",
4217         local_player->present);
4218 #endif
4219
4220   // set focus to local player for network games, else to all players
4221   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
4222   game.centered_player_nr_next = game.centered_player_nr;
4223   game.set_centered_player = FALSE;
4224   game.set_centered_player_wrap = FALSE;
4225
4226   if (network_playing && tape.recording)
4227   {
4228     // store client dependent player focus when recording network games
4229     tape.centered_player_nr_next = game.centered_player_nr_next;
4230     tape.set_centered_player = TRUE;
4231   }
4232
4233   if (tape.playing)
4234   {
4235     // when playing a tape, eliminate all players who do not participate
4236
4237 #if USE_NEW_PLAYER_ASSIGNMENTS
4238
4239     if (!game.team_mode)
4240     {
4241       for (i = 0; i < MAX_PLAYERS; i++)
4242       {
4243         if (stored_player[i].active &&
4244             !tape.player_participates[map_player_action[i]])
4245         {
4246           struct PlayerInfo *player = &stored_player[i];
4247           int jx = player->jx, jy = player->jy;
4248
4249 #if DEBUG_INIT_PLAYER
4250           Debug("game:init:player", "Removing player %d at (%d, %d)",
4251                 i + 1, jx, jy);
4252 #endif
4253
4254           player->active = FALSE;
4255           StorePlayer[jx][jy] = 0;
4256           Tile[jx][jy] = EL_EMPTY;
4257         }
4258       }
4259     }
4260
4261 #else
4262
4263     for (i = 0; i < MAX_PLAYERS; i++)
4264     {
4265       if (stored_player[i].active &&
4266           !tape.player_participates[i])
4267       {
4268         struct PlayerInfo *player = &stored_player[i];
4269         int jx = player->jx, jy = player->jy;
4270
4271         player->active = FALSE;
4272         StorePlayer[jx][jy] = 0;
4273         Tile[jx][jy] = EL_EMPTY;
4274       }
4275     }
4276 #endif
4277   }
4278   else if (!network.enabled && !game.team_mode)         // && !tape.playing
4279   {
4280     // when in single player mode, eliminate all but the local player
4281
4282     for (i = 0; i < MAX_PLAYERS; i++)
4283     {
4284       struct PlayerInfo *player = &stored_player[i];
4285
4286       if (player->active && player != local_player)
4287       {
4288         int jx = player->jx, jy = player->jy;
4289
4290         player->active = FALSE;
4291         player->present = FALSE;
4292
4293         StorePlayer[jx][jy] = 0;
4294         Tile[jx][jy] = EL_EMPTY;
4295       }
4296     }
4297   }
4298
4299   for (i = 0; i < MAX_PLAYERS; i++)
4300     if (stored_player[i].active)
4301       game.players_still_needed++;
4302
4303   if (level.solved_by_one_player)
4304     game.players_still_needed = 1;
4305
4306   // when recording the game, store which players take part in the game
4307   if (tape.recording)
4308   {
4309 #if USE_NEW_PLAYER_ASSIGNMENTS
4310     for (i = 0; i < MAX_PLAYERS; i++)
4311       if (stored_player[i].connected)
4312         tape.player_participates[i] = TRUE;
4313 #else
4314     for (i = 0; i < MAX_PLAYERS; i++)
4315       if (stored_player[i].active)
4316         tape.player_participates[i] = TRUE;
4317 #endif
4318   }
4319
4320 #if DEBUG_INIT_PLAYER
4321   DebugPrintPlayerStatus("Player status after player assignment (final stage)");
4322 #endif
4323
4324   if (BorderElement == EL_EMPTY)
4325   {
4326     SBX_Left = 0;
4327     SBX_Right = lev_fieldx - SCR_FIELDX;
4328     SBY_Upper = 0;
4329     SBY_Lower = lev_fieldy - SCR_FIELDY;
4330   }
4331   else
4332   {
4333     SBX_Left = -1;
4334     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4335     SBY_Upper = -1;
4336     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4337   }
4338
4339   if (full_lev_fieldx <= SCR_FIELDX)
4340     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4341   if (full_lev_fieldy <= SCR_FIELDY)
4342     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4343
4344   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4345     SBX_Left--;
4346   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4347     SBY_Upper--;
4348
4349   // if local player not found, look for custom element that might create
4350   // the player (make some assumptions about the right custom element)
4351   if (!local_player->present)
4352   {
4353     int start_x = 0, start_y = 0;
4354     int found_rating = 0;
4355     int found_element = EL_UNDEFINED;
4356     int player_nr = local_player->index_nr;
4357
4358     SCAN_PLAYFIELD(x, y)
4359     {
4360       int element = Tile[x][y];
4361       int content;
4362       int xx, yy;
4363       boolean is_player;
4364
4365       if (level.use_start_element[player_nr] &&
4366           level.start_element[player_nr] == element &&
4367           found_rating < 4)
4368       {
4369         start_x = x;
4370         start_y = y;
4371
4372         found_rating = 4;
4373         found_element = element;
4374       }
4375
4376       if (!IS_CUSTOM_ELEMENT(element))
4377         continue;
4378
4379       if (CAN_CHANGE(element))
4380       {
4381         for (i = 0; i < element_info[element].num_change_pages; i++)
4382         {
4383           // check for player created from custom element as single target
4384           content = element_info[element].change_page[i].target_element;
4385           is_player = IS_PLAYER_ELEMENT(content);
4386
4387           if (is_player && (found_rating < 3 ||
4388                             (found_rating == 3 && element < found_element)))
4389           {
4390             start_x = x;
4391             start_y = y;
4392
4393             found_rating = 3;
4394             found_element = element;
4395           }
4396         }
4397       }
4398
4399       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4400       {
4401         // check for player created from custom element as explosion content
4402         content = element_info[element].content.e[xx][yy];
4403         is_player = IS_PLAYER_ELEMENT(content);
4404
4405         if (is_player && (found_rating < 2 ||
4406                           (found_rating == 2 && element < found_element)))
4407         {
4408           start_x = x + xx - 1;
4409           start_y = y + yy - 1;
4410
4411           found_rating = 2;
4412           found_element = element;
4413         }
4414
4415         if (!CAN_CHANGE(element))
4416           continue;
4417
4418         for (i = 0; i < element_info[element].num_change_pages; i++)
4419         {
4420           // check for player created from custom element as extended target
4421           content =
4422             element_info[element].change_page[i].target_content.e[xx][yy];
4423
4424           is_player = IS_PLAYER_ELEMENT(content);
4425
4426           if (is_player && (found_rating < 1 ||
4427                             (found_rating == 1 && element < found_element)))
4428           {
4429             start_x = x + xx - 1;
4430             start_y = y + yy - 1;
4431
4432             found_rating = 1;
4433             found_element = element;
4434           }
4435         }
4436       }
4437     }
4438
4439     scroll_x = SCROLL_POSITION_X(start_x);
4440     scroll_y = SCROLL_POSITION_Y(start_y);
4441   }
4442   else
4443   {
4444     scroll_x = SCROLL_POSITION_X(local_player->jx);
4445     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4446   }
4447
4448   if (game.forced_scroll_x != ARG_UNDEFINED_VALUE)
4449     scroll_x = game.forced_scroll_x;
4450   if (game.forced_scroll_y != ARG_UNDEFINED_VALUE)
4451     scroll_y = game.forced_scroll_y;
4452
4453   // !!! FIX THIS (START) !!!
4454   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4455   {
4456     InitGameEngine_EM();
4457   }
4458   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4459   {
4460     InitGameEngine_SP();
4461   }
4462   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4463   {
4464     InitGameEngine_MM();
4465   }
4466   else
4467   {
4468     DrawLevel(REDRAW_FIELD);
4469     DrawAllPlayers();
4470
4471     // after drawing the level, correct some elements
4472     if (game.timegate_time_left == 0)
4473       CloseAllOpenTimegates();
4474   }
4475
4476   // blit playfield from scroll buffer to normal back buffer for fading in
4477   BlitScreenToBitmap(backbuffer);
4478   // !!! FIX THIS (END) !!!
4479
4480   DrawMaskedBorder(fade_mask);
4481
4482   FadeIn(fade_mask);
4483
4484 #if 1
4485   // full screen redraw is required at this point in the following cases:
4486   // - special editor door undrawn when game was started from level editor
4487   // - drawing area (playfield) was changed and has to be removed completely
4488   redraw_mask = REDRAW_ALL;
4489   BackToFront();
4490 #endif
4491
4492   if (!game.restart_level)
4493   {
4494     // copy default game door content to main double buffer
4495
4496     // !!! CHECK AGAIN !!!
4497     SetPanelBackground();
4498     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4499     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4500   }
4501
4502   SetPanelBackground();
4503   SetDrawBackgroundMask(REDRAW_DOOR_1);
4504
4505   UpdateAndDisplayGameControlValues();
4506
4507   if (!game.restart_level)
4508   {
4509     UnmapGameButtons();
4510     UnmapTapeButtons();
4511
4512     FreeGameButtons();
4513     CreateGameButtons();
4514
4515     MapGameButtons();
4516     MapTapeButtons();
4517
4518     // copy actual game door content to door double buffer for OpenDoor()
4519     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4520
4521     OpenDoor(DOOR_OPEN_ALL);
4522
4523     KeyboardAutoRepeatOffUnlessAutoplay();
4524
4525 #if DEBUG_INIT_PLAYER
4526     DebugPrintPlayerStatus("Player status (final)");
4527 #endif
4528   }
4529
4530   UnmapAllGadgets();
4531
4532   MapGameButtons();
4533   MapTapeButtons();
4534
4535   if (!game.restart_level && !tape.playing)
4536   {
4537     LevelStats_incPlayed(level_nr);
4538
4539     SaveLevelSetup_SeriesInfo();
4540   }
4541
4542   game.restart_level = FALSE;
4543
4544   game.request_active = FALSE;
4545   game.request_active_or_moving = FALSE;
4546
4547   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4548     InitGameActions_MM();
4549
4550   SaveEngineSnapshotToListInitial();
4551
4552   if (!game.restart_level)
4553   {
4554     PlaySound(SND_GAME_STARTING);
4555
4556     if (setup.sound_music)
4557       PlayLevelMusic();
4558   }
4559
4560   SetPlayfieldMouseCursorEnabled(!game.use_mouse_actions);
4561 }
4562
4563 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4564                         int actual_player_x, int actual_player_y)
4565 {
4566   // this is used for non-R'n'D game engines to update certain engine values
4567
4568   // needed to determine if sounds are played within the visible screen area
4569   scroll_x = actual_scroll_x;
4570   scroll_y = actual_scroll_y;
4571
4572   // needed to get player position for "follow finger" playing input method
4573   local_player->jx = actual_player_x;
4574   local_player->jy = actual_player_y;
4575 }
4576
4577 void InitMovDir(int x, int y)
4578 {
4579   int i, element = Tile[x][y];
4580   static int xy[4][2] =
4581   {
4582     {  0, +1 },
4583     { +1,  0 },
4584     {  0, -1 },
4585     { -1,  0 }
4586   };
4587   static int direction[3][4] =
4588   {
4589     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4590     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4591     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4592   };
4593
4594   switch (element)
4595   {
4596     case EL_BUG_RIGHT:
4597     case EL_BUG_UP:
4598     case EL_BUG_LEFT:
4599     case EL_BUG_DOWN:
4600       Tile[x][y] = EL_BUG;
4601       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4602       break;
4603
4604     case EL_SPACESHIP_RIGHT:
4605     case EL_SPACESHIP_UP:
4606     case EL_SPACESHIP_LEFT:
4607     case EL_SPACESHIP_DOWN:
4608       Tile[x][y] = EL_SPACESHIP;
4609       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4610       break;
4611
4612     case EL_BD_BUTTERFLY_RIGHT:
4613     case EL_BD_BUTTERFLY_UP:
4614     case EL_BD_BUTTERFLY_LEFT:
4615     case EL_BD_BUTTERFLY_DOWN:
4616       Tile[x][y] = EL_BD_BUTTERFLY;
4617       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4618       break;
4619
4620     case EL_BD_FIREFLY_RIGHT:
4621     case EL_BD_FIREFLY_UP:
4622     case EL_BD_FIREFLY_LEFT:
4623     case EL_BD_FIREFLY_DOWN:
4624       Tile[x][y] = EL_BD_FIREFLY;
4625       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4626       break;
4627
4628     case EL_PACMAN_RIGHT:
4629     case EL_PACMAN_UP:
4630     case EL_PACMAN_LEFT:
4631     case EL_PACMAN_DOWN:
4632       Tile[x][y] = EL_PACMAN;
4633       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4634       break;
4635
4636     case EL_YAMYAM_LEFT:
4637     case EL_YAMYAM_RIGHT:
4638     case EL_YAMYAM_UP:
4639     case EL_YAMYAM_DOWN:
4640       Tile[x][y] = EL_YAMYAM;
4641       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4642       break;
4643
4644     case EL_SP_SNIKSNAK:
4645       MovDir[x][y] = MV_UP;
4646       break;
4647
4648     case EL_SP_ELECTRON:
4649       MovDir[x][y] = MV_LEFT;
4650       break;
4651
4652     case EL_MOLE_LEFT:
4653     case EL_MOLE_RIGHT:
4654     case EL_MOLE_UP:
4655     case EL_MOLE_DOWN:
4656       Tile[x][y] = EL_MOLE;
4657       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4658       break;
4659
4660     case EL_SPRING_LEFT:
4661     case EL_SPRING_RIGHT:
4662       Tile[x][y] = EL_SPRING;
4663       MovDir[x][y] = direction[2][element - EL_SPRING_LEFT];
4664       break;
4665
4666     default:
4667       if (IS_CUSTOM_ELEMENT(element))
4668       {
4669         struct ElementInfo *ei = &element_info[element];
4670         int move_direction_initial = ei->move_direction_initial;
4671         int move_pattern = ei->move_pattern;
4672
4673         if (move_direction_initial == MV_START_PREVIOUS)
4674         {
4675           if (MovDir[x][y] != MV_NONE)
4676             return;
4677
4678           move_direction_initial = MV_START_AUTOMATIC;
4679         }
4680
4681         if (move_direction_initial == MV_START_RANDOM)
4682           MovDir[x][y] = 1 << RND(4);
4683         else if (move_direction_initial & MV_ANY_DIRECTION)
4684           MovDir[x][y] = move_direction_initial;
4685         else if (move_pattern == MV_ALL_DIRECTIONS ||
4686                  move_pattern == MV_TURNING_LEFT ||
4687                  move_pattern == MV_TURNING_RIGHT ||
4688                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4689                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4690                  move_pattern == MV_TURNING_RANDOM)
4691           MovDir[x][y] = 1 << RND(4);
4692         else if (move_pattern == MV_HORIZONTAL)
4693           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4694         else if (move_pattern == MV_VERTICAL)
4695           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4696         else if (move_pattern & MV_ANY_DIRECTION)
4697           MovDir[x][y] = element_info[element].move_pattern;
4698         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4699                  move_pattern == MV_ALONG_RIGHT_SIDE)
4700         {
4701           // use random direction as default start direction
4702           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4703             MovDir[x][y] = 1 << RND(4);
4704
4705           for (i = 0; i < NUM_DIRECTIONS; i++)
4706           {
4707             int x1 = x + xy[i][0];
4708             int y1 = y + xy[i][1];
4709
4710             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4711             {
4712               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4713                 MovDir[x][y] = direction[0][i];
4714               else
4715                 MovDir[x][y] = direction[1][i];
4716
4717               break;
4718             }
4719           }
4720         }                
4721       }
4722       else
4723       {
4724         MovDir[x][y] = 1 << RND(4);
4725
4726         if (element != EL_BUG &&
4727             element != EL_SPACESHIP &&
4728             element != EL_BD_BUTTERFLY &&
4729             element != EL_BD_FIREFLY)
4730           break;
4731
4732         for (i = 0; i < NUM_DIRECTIONS; i++)
4733         {
4734           int x1 = x + xy[i][0];
4735           int y1 = y + xy[i][1];
4736
4737           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4738           {
4739             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4740             {
4741               MovDir[x][y] = direction[0][i];
4742               break;
4743             }
4744             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4745                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4746             {
4747               MovDir[x][y] = direction[1][i];
4748               break;
4749             }
4750           }
4751         }
4752       }
4753       break;
4754   }
4755
4756   GfxDir[x][y] = MovDir[x][y];
4757 }
4758
4759 void InitAmoebaNr(int x, int y)
4760 {
4761   int i;
4762   int group_nr = AmoebaNeighbourNr(x, y);
4763
4764   if (group_nr == 0)
4765   {
4766     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4767     {
4768       if (AmoebaCnt[i] == 0)
4769       {
4770         group_nr = i;
4771         break;
4772       }
4773     }
4774   }
4775
4776   AmoebaNr[x][y] = group_nr;
4777   AmoebaCnt[group_nr]++;
4778   AmoebaCnt2[group_nr]++;
4779 }
4780
4781 static void LevelSolved_SetFinalGameValues(void)
4782 {
4783   game.time_final = (game.no_level_time_limit ? TimePlayed : TimeLeft);
4784   game.score_time_final = (level.use_step_counter ? TimePlayed :
4785                            TimePlayed * FRAMES_PER_SECOND + TimeFrames);
4786
4787   game.score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4788                       game_em.lev->score :
4789                       level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4790                       game_mm.score :
4791                       game.score);
4792
4793   game.health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4794                        MM_HEALTH(game_mm.laser_overload_value) :
4795                        game.health);
4796
4797   game.LevelSolved_CountingTime = game.time_final;
4798   game.LevelSolved_CountingScore = game.score_final;
4799   game.LevelSolved_CountingHealth = game.health_final;
4800 }
4801
4802 static void LevelSolved_DisplayFinalGameValues(int time, int score, int health)
4803 {
4804   game.LevelSolved_CountingTime = time;
4805   game.LevelSolved_CountingScore = score;
4806   game.LevelSolved_CountingHealth = health;
4807
4808   game_panel_controls[GAME_PANEL_TIME].value = time;
4809   game_panel_controls[GAME_PANEL_SCORE].value = score;
4810   game_panel_controls[GAME_PANEL_HEALTH].value = health;
4811
4812   DisplayGameControlValues();
4813 }
4814
4815 static void LevelSolved(void)
4816 {
4817   if (level.game_engine_type == GAME_ENGINE_TYPE_RND &&
4818       game.players_still_needed > 0)
4819     return;
4820
4821   game.LevelSolved = TRUE;
4822   game.GameOver = TRUE;
4823
4824   tape.solved = TRUE;
4825
4826   // needed here to display correct panel values while player walks into exit
4827   LevelSolved_SetFinalGameValues();
4828 }
4829
4830 void GameWon(void)
4831 {
4832   static int time_count_steps;
4833   static int time, time_final;
4834   static float score, score_final; // needed for time score < 10 for 10 seconds
4835   static int health, health_final;
4836   static int game_over_delay_1 = 0;
4837   static int game_over_delay_2 = 0;
4838   static int game_over_delay_3 = 0;
4839   int time_score_base = MIN(MAX(1, level.time_score_base), 10);
4840   float time_score = (float)level.score[SC_TIME_BONUS] / time_score_base;
4841
4842   if (!game.LevelSolved_GameWon)
4843   {
4844     int i;
4845
4846     // do not start end game actions before the player stops moving (to exit)
4847     if (local_player->active && local_player->MovPos)
4848       return;
4849
4850     // calculate final game values after player finished walking into exit
4851     LevelSolved_SetFinalGameValues();
4852
4853     game.LevelSolved_GameWon = TRUE;
4854     game.LevelSolved_SaveTape = tape.recording;
4855     game.LevelSolved_SaveScore = !tape.playing;
4856
4857     if (!tape.playing)
4858     {
4859       LevelStats_incSolved(level_nr);
4860
4861       SaveLevelSetup_SeriesInfo();
4862     }
4863
4864     if (tape.auto_play)         // tape might already be stopped here
4865       tape.auto_play_level_solved = TRUE;
4866
4867     TapeStop();
4868
4869     game_over_delay_1 = FRAMES_PER_SECOND;      // delay before counting time
4870     game_over_delay_2 = FRAMES_PER_SECOND / 2;  // delay before counting health
4871     game_over_delay_3 = FRAMES_PER_SECOND;      // delay before ending the game
4872
4873     time = time_final = game.time_final;
4874     score = score_final = game.score_final;
4875     health = health_final = game.health_final;
4876
4877     // update game panel values before (delayed) counting of score (if any)
4878     LevelSolved_DisplayFinalGameValues(time, score, health);
4879
4880     // if level has time score defined, calculate new final game values
4881     if (time_score > 0)
4882     {
4883       int time_final_max = 999;
4884       int time_frames_final_max = time_final_max * FRAMES_PER_SECOND;
4885       int time_frames = 0;
4886       int time_frames_left = TimeLeft * FRAMES_PER_SECOND - TimeFrames;
4887       int time_frames_played = TimePlayed * FRAMES_PER_SECOND + TimeFrames;
4888
4889       if (TimeLeft > 0)
4890       {
4891         time_final = 0;
4892         time_frames = time_frames_left;
4893       }
4894       else if (game.no_level_time_limit && TimePlayed < time_final_max)
4895       {
4896         time_final = time_final_max;
4897         time_frames = time_frames_final_max - time_frames_played;
4898       }
4899
4900       score_final += time_score * time_frames / FRAMES_PER_SECOND + 0.5;
4901
4902       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4903
4904       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4905       {
4906         health_final = 0;
4907         score_final += health * time_score;
4908       }
4909
4910       game.score_final = score_final;
4911       game.health_final = health_final;
4912     }
4913
4914     // if not counting score after game, immediately update game panel values
4915     if (level_editor_test_game || !setup.count_score_after_game)
4916     {
4917       time = time_final;
4918       score = score_final;
4919
4920       LevelSolved_DisplayFinalGameValues(time, score, health);
4921     }
4922
4923     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4924     {
4925       // check if last player has left the level
4926       if (game.exit_x >= 0 &&
4927           game.exit_y >= 0)
4928       {
4929         int x = game.exit_x;
4930         int y = game.exit_y;
4931         int element = Tile[x][y];
4932
4933         // close exit door after last player
4934         if ((game.all_players_gone &&
4935              (element == EL_EXIT_OPEN ||
4936               element == EL_SP_EXIT_OPEN ||
4937               element == EL_STEEL_EXIT_OPEN)) ||
4938             element == EL_EM_EXIT_OPEN ||
4939             element == EL_EM_STEEL_EXIT_OPEN)
4940         {
4941
4942           Tile[x][y] =
4943             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4944              element == EL_EM_EXIT_OPEN         ? EL_EM_EXIT_CLOSING :
4945              element == EL_SP_EXIT_OPEN         ? EL_SP_EXIT_CLOSING:
4946              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4947              EL_EM_STEEL_EXIT_CLOSING);
4948
4949           PlayLevelSoundElementAction(x, y, element, ACTION_CLOSING);
4950         }
4951
4952         // player disappears
4953         DrawLevelField(x, y);
4954       }
4955
4956       for (i = 0; i < MAX_PLAYERS; i++)
4957       {
4958         struct PlayerInfo *player = &stored_player[i];
4959
4960         if (player->present)
4961         {
4962           RemovePlayer(player);
4963
4964           // player disappears
4965           DrawLevelField(player->jx, player->jy);
4966         }
4967       }
4968     }
4969
4970     PlaySound(SND_GAME_WINNING);
4971   }
4972
4973   if (setup.count_score_after_game)
4974   {
4975     if (time != time_final)
4976     {
4977       if (game_over_delay_1 > 0)
4978       {
4979         game_over_delay_1--;
4980
4981         return;
4982       }
4983
4984       int time_to_go = ABS(time_final - time);
4985       int time_count_dir = (time < time_final ? +1 : -1);
4986
4987       if (time_to_go < time_count_steps)
4988         time_count_steps = 1;
4989
4990       time  += time_count_steps * time_count_dir;
4991       score += time_count_steps * time_score;
4992
4993       // set final score to correct rounding differences after counting score
4994       if (time == time_final)
4995         score = score_final;
4996
4997       LevelSolved_DisplayFinalGameValues(time, score, health);
4998
4999       if (time == time_final)
5000         StopSound(SND_GAME_LEVELTIME_BONUS);
5001       else if (setup.sound_loops)
5002         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
5003       else
5004         PlaySound(SND_GAME_LEVELTIME_BONUS);
5005
5006       return;
5007     }
5008
5009     if (health != health_final)
5010     {
5011       if (game_over_delay_2 > 0)
5012       {
5013         game_over_delay_2--;
5014
5015         return;
5016       }
5017
5018       int health_count_dir = (health < health_final ? +1 : -1);
5019
5020       health += health_count_dir;
5021       score  += time_score;
5022
5023       LevelSolved_DisplayFinalGameValues(time, score, health);
5024
5025       if (health == health_final)
5026         StopSound(SND_GAME_LEVELTIME_BONUS);
5027       else if (setup.sound_loops)
5028         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
5029       else
5030         PlaySound(SND_GAME_LEVELTIME_BONUS);
5031
5032       return;
5033     }
5034   }
5035
5036   game.panel.active = FALSE;
5037
5038   if (game_over_delay_3 > 0)
5039   {
5040     game_over_delay_3--;
5041
5042     return;
5043   }
5044
5045   GameEnd();
5046 }
5047
5048 void GameEnd(void)
5049 {
5050   // used instead of "level_nr" (needed for network games)
5051   int last_level_nr = levelset.level_nr;
5052   boolean tape_saved = FALSE;
5053
5054   game.LevelSolved_GameEnd = TRUE;
5055
5056   if (game.LevelSolved_SaveTape && !score_info_tape_play)
5057   {
5058     // make sure that request dialog to save tape does not open door again
5059     if (!global.use_envelope_request)
5060       CloseDoor(DOOR_CLOSE_1);
5061
5062     // ask to save tape
5063     tape_saved = SaveTapeChecked_LevelSolved(tape.level_nr);
5064
5065     // set unique basename for score tape (also saved in high score table)
5066     strcpy(tape.score_tape_basename, getScoreTapeBasename(setup.player_name));
5067   }
5068
5069   // if no tape is to be saved, close both doors simultaneously
5070   CloseDoor(DOOR_CLOSE_ALL);
5071
5072   if (level_editor_test_game || score_info_tape_play)
5073   {
5074     SetGameStatus(GAME_MODE_MAIN);
5075
5076     DrawMainMenu();
5077
5078     return;
5079   }
5080
5081   if (!game.LevelSolved_SaveScore)
5082   {
5083     SetGameStatus(GAME_MODE_MAIN);
5084
5085     DrawMainMenu();
5086
5087     return;
5088   }
5089
5090   if (level_nr == leveldir_current->handicap_level)
5091   {
5092     leveldir_current->handicap_level++;
5093
5094     SaveLevelSetup_SeriesInfo();
5095   }
5096
5097   // save score and score tape before potentially erasing tape below
5098   NewHighScore(last_level_nr, tape_saved);
5099
5100   if (setup.increment_levels &&
5101       level_nr < leveldir_current->last_level &&
5102       !network_playing)
5103   {
5104     level_nr++;         // advance to next level
5105     TapeErase();        // start with empty tape
5106
5107     if (setup.auto_play_next_level)
5108     {
5109       scores.continue_playing = TRUE;
5110       scores.next_level_nr = level_nr;
5111
5112       LoadLevel(level_nr);
5113
5114       SaveLevelSetup_SeriesInfo();
5115     }
5116   }
5117
5118   if (scores.last_added >= 0 && setup.show_scores_after_game)
5119   {
5120     SetGameStatus(GAME_MODE_SCORES);
5121
5122     DrawHallOfFame(last_level_nr);
5123   }
5124   else if (scores.continue_playing)
5125   {
5126     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
5127   }
5128   else
5129   {
5130     SetGameStatus(GAME_MODE_MAIN);
5131
5132     DrawMainMenu();
5133   }
5134 }
5135
5136 static int addScoreEntry(struct ScoreInfo *list, struct ScoreEntry *new_entry,
5137                          boolean one_score_entry_per_name)
5138 {
5139   int i;
5140
5141   if (strEqual(new_entry->name, EMPTY_PLAYER_NAME))
5142     return -1;
5143
5144   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
5145   {
5146     struct ScoreEntry *entry = &list->entry[i];
5147     boolean score_is_better = (new_entry->score >  entry->score);
5148     boolean score_is_equal  = (new_entry->score == entry->score);
5149     boolean time_is_better  = (new_entry->time  <  entry->time);
5150     boolean time_is_equal   = (new_entry->time  == entry->time);
5151     boolean better_by_score = (score_is_better ||
5152                                (score_is_equal && time_is_better));
5153     boolean better_by_time  = (time_is_better ||
5154                                (time_is_equal && score_is_better));
5155     boolean is_better = (level.rate_time_over_score ? better_by_time :
5156                          better_by_score);
5157     boolean entry_is_empty = (entry->score == 0 &&
5158                               entry->time == 0);
5159
5160     // prevent adding server score entries if also existing in local score file
5161     // (special case: historic score entries have an empty tape basename entry)
5162     if (strEqual(new_entry->tape_basename, entry->tape_basename) &&
5163         !strEqual(new_entry->tape_basename, UNDEFINED_FILENAME))
5164     {
5165       // add fields from server score entry not stored in local score entry
5166       // (currently, this means setting platform, version and country fields;
5167       // in rare cases, this may also correct an invalid score value, as
5168       // historic scores might have been truncated to 16-bit values locally)
5169       *entry = *new_entry;
5170
5171       return -1;
5172     }
5173
5174     if (is_better || entry_is_empty)
5175     {
5176       // player has made it to the hall of fame
5177
5178       if (i < MAX_SCORE_ENTRIES - 1)
5179       {
5180         int m = MAX_SCORE_ENTRIES - 1;
5181         int l;
5182
5183         if (one_score_entry_per_name)
5184         {
5185           for (l = i; l < MAX_SCORE_ENTRIES; l++)
5186             if (strEqual(list->entry[l].name, new_entry->name))
5187               m = l;
5188
5189           if (m == i)   // player's new highscore overwrites his old one
5190             goto put_into_list;
5191         }
5192
5193         for (l = m; l > i; l--)
5194           list->entry[l] = list->entry[l - 1];
5195       }
5196
5197       put_into_list:
5198
5199       *entry = *new_entry;
5200
5201       return i;
5202     }
5203     else if (one_score_entry_per_name &&
5204              strEqual(entry->name, new_entry->name))
5205     {
5206       // player already in high score list with better score or time
5207
5208       return -1;
5209     }
5210   }
5211
5212   // special case: new score is beyond the last high score list position
5213   return MAX_SCORE_ENTRIES;
5214 }
5215
5216 void NewHighScore(int level_nr, boolean tape_saved)
5217 {
5218   struct ScoreEntry new_entry = {{ 0 }}; // (prevent warning from GCC bug 53119)
5219   boolean one_per_name = FALSE;
5220
5221   strncpy(new_entry.tape_basename, tape.score_tape_basename, MAX_FILENAME_LEN);
5222   strncpy(new_entry.name, setup.player_name, MAX_PLAYER_NAME_LEN);
5223
5224   new_entry.score = game.score_final;
5225   new_entry.time = game.score_time_final;
5226
5227   LoadScore(level_nr);
5228
5229   scores.last_added = addScoreEntry(&scores, &new_entry, one_per_name);
5230
5231   if (scores.last_added >= MAX_SCORE_ENTRIES)
5232   {
5233     scores.last_added = MAX_SCORE_ENTRIES - 1;
5234     scores.force_last_added = TRUE;
5235
5236     scores.entry[scores.last_added] = new_entry;
5237
5238     // store last added local score entry (before merging server scores)
5239     scores.last_added_local = scores.last_added;
5240
5241     return;
5242   }
5243
5244   if (scores.last_added < 0)
5245     return;
5246
5247   SaveScore(level_nr);
5248
5249   // store last added local score entry (before merging server scores)
5250   scores.last_added_local = scores.last_added;
5251
5252   if (!game.LevelSolved_SaveTape)
5253     return;
5254
5255   SaveScoreTape(level_nr);
5256
5257   if (setup.ask_for_using_api_server)
5258   {
5259     setup.use_api_server =
5260       Request("Upload your score and tape to the high score server?", REQ_ASK);
5261
5262     if (!setup.use_api_server)
5263       Request("Not using high score server! Use setup menu to enable again!",
5264               REQ_CONFIRM);
5265
5266     runtime.use_api_server = setup.use_api_server;
5267
5268     // after asking for using API server once, do not ask again
5269     setup.ask_for_using_api_server = FALSE;
5270
5271     SaveSetup_ServerSetup();
5272   }
5273
5274   SaveServerScore(level_nr, tape_saved);
5275 }
5276
5277 void MergeServerScore(void)
5278 {
5279   struct ScoreEntry last_added_entry;
5280   boolean one_per_name = FALSE;
5281   int i;
5282
5283   if (scores.last_added >= 0)
5284     last_added_entry = scores.entry[scores.last_added];
5285
5286   for (i = 0; i < server_scores.num_entries; i++)
5287   {
5288     int pos = addScoreEntry(&scores, &server_scores.entry[i], one_per_name);
5289
5290     if (pos >= 0 && pos <= scores.last_added)
5291       scores.last_added++;
5292   }
5293
5294   if (scores.last_added >= MAX_SCORE_ENTRIES)
5295   {
5296     scores.last_added = MAX_SCORE_ENTRIES - 1;
5297     scores.force_last_added = TRUE;
5298
5299     scores.entry[scores.last_added] = last_added_entry;
5300   }
5301 }
5302
5303 static int getElementMoveStepsizeExt(int x, int y, int direction)
5304 {
5305   int element = Tile[x][y];
5306   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5307   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5308   int horiz_move = (dx != 0);
5309   int sign = (horiz_move ? dx : dy);
5310   int step = sign * element_info[element].move_stepsize;
5311
5312   // special values for move stepsize for spring and things on conveyor belt
5313   if (horiz_move)
5314   {
5315     if (CAN_FALL(element) &&
5316         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Tile[x][y + 1]))
5317       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5318     else if (element == EL_SPRING)
5319       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5320   }
5321
5322   return step;
5323 }
5324
5325 static int getElementMoveStepsize(int x, int y)
5326 {
5327   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5328 }
5329
5330 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5331 {
5332   if (player->GfxAction != action || player->GfxDir != dir)
5333   {
5334     player->GfxAction = action;
5335     player->GfxDir = dir;
5336     player->Frame = 0;
5337     player->StepFrame = 0;
5338   }
5339 }
5340
5341 static void ResetGfxFrame(int x, int y)
5342 {
5343   // profiling showed that "autotest" spends 10~20% of its time in this function
5344   if (DrawingDeactivatedField())
5345     return;
5346
5347   int element = Tile[x][y];
5348   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5349
5350   if (graphic_info[graphic].anim_global_sync)
5351     GfxFrame[x][y] = FrameCounter;
5352   else if (graphic_info[graphic].anim_global_anim_sync)
5353     GfxFrame[x][y] = getGlobalAnimSyncFrame();
5354   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5355     GfxFrame[x][y] = CustomValue[x][y];
5356   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5357     GfxFrame[x][y] = element_info[element].collect_score;
5358   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5359     GfxFrame[x][y] = ChangeDelay[x][y];
5360 }
5361
5362 static void ResetGfxAnimation(int x, int y)
5363 {
5364   GfxAction[x][y] = ACTION_DEFAULT;
5365   GfxDir[x][y] = MovDir[x][y];
5366   GfxFrame[x][y] = 0;
5367
5368   ResetGfxFrame(x, y);
5369 }
5370
5371 static void ResetRandomAnimationValue(int x, int y)
5372 {
5373   GfxRandom[x][y] = INIT_GFX_RANDOM();
5374 }
5375
5376 static void InitMovingField(int x, int y, int direction)
5377 {
5378   int element = Tile[x][y];
5379   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5380   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5381   int newx = x + dx;
5382   int newy = y + dy;
5383   boolean is_moving_before, is_moving_after;
5384
5385   // check if element was/is moving or being moved before/after mode change
5386   is_moving_before = (WasJustMoving[x][y] != 0);
5387   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5388
5389   // reset animation only for moving elements which change direction of moving
5390   // or which just started or stopped moving
5391   // (else CEs with property "can move" / "not moving" are reset each frame)
5392   if (is_moving_before != is_moving_after ||
5393       direction != MovDir[x][y])
5394     ResetGfxAnimation(x, y);
5395
5396   MovDir[x][y] = direction;
5397   GfxDir[x][y] = direction;
5398
5399   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5400                      direction == MV_DOWN && CAN_FALL(element) ?
5401                      ACTION_FALLING : ACTION_MOVING);
5402
5403   // this is needed for CEs with property "can move" / "not moving"
5404
5405   if (is_moving_after)
5406   {
5407     if (Tile[newx][newy] == EL_EMPTY)
5408       Tile[newx][newy] = EL_BLOCKED;
5409
5410     MovDir[newx][newy] = MovDir[x][y];
5411
5412     CustomValue[newx][newy] = CustomValue[x][y];
5413
5414     GfxFrame[newx][newy] = GfxFrame[x][y];
5415     GfxRandom[newx][newy] = GfxRandom[x][y];
5416     GfxAction[newx][newy] = GfxAction[x][y];
5417     GfxDir[newx][newy] = GfxDir[x][y];
5418   }
5419 }
5420
5421 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5422 {
5423   int direction = MovDir[x][y];
5424   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5425   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5426
5427   *goes_to_x = newx;
5428   *goes_to_y = newy;
5429 }
5430
5431 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5432 {
5433   int direction = MovDir[x][y];
5434   int oldx = x + (direction & MV_LEFT ? +1 : direction & MV_RIGHT ? -1 : 0);
5435   int oldy = y + (direction & MV_UP   ? +1 : direction & MV_DOWN  ? -1 : 0);
5436
5437   *comes_from_x = oldx;
5438   *comes_from_y = oldy;
5439 }
5440
5441 static int MovingOrBlocked2Element(int x, int y)
5442 {
5443   int element = Tile[x][y];
5444
5445   if (element == EL_BLOCKED)
5446   {
5447     int oldx, oldy;
5448
5449     Blocked2Moving(x, y, &oldx, &oldy);
5450
5451     return Tile[oldx][oldy];
5452   }
5453
5454   return element;
5455 }
5456
5457 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5458 {
5459   // like MovingOrBlocked2Element(), but if element is moving
5460   // and (x, y) is the field the moving element is just leaving,
5461   // return EL_BLOCKED instead of the element value
5462   int element = Tile[x][y];
5463
5464   if (IS_MOVING(x, y))
5465   {
5466     if (element == EL_BLOCKED)
5467     {
5468       int oldx, oldy;
5469
5470       Blocked2Moving(x, y, &oldx, &oldy);
5471       return Tile[oldx][oldy];
5472     }
5473     else
5474       return EL_BLOCKED;
5475   }
5476   else
5477     return element;
5478 }
5479
5480 static void RemoveField(int x, int y)
5481 {
5482   Tile[x][y] = EL_EMPTY;
5483
5484   MovPos[x][y] = 0;
5485   MovDir[x][y] = 0;
5486   MovDelay[x][y] = 0;
5487
5488   CustomValue[x][y] = 0;
5489
5490   AmoebaNr[x][y] = 0;
5491   ChangeDelay[x][y] = 0;
5492   ChangePage[x][y] = -1;
5493   Pushed[x][y] = FALSE;
5494
5495   GfxElement[x][y] = EL_UNDEFINED;
5496   GfxAction[x][y] = ACTION_DEFAULT;
5497   GfxDir[x][y] = MV_NONE;
5498 }
5499
5500 static void RemoveMovingField(int x, int y)
5501 {
5502   int oldx = x, oldy = y, newx = x, newy = y;
5503   int element = Tile[x][y];
5504   int next_element = EL_UNDEFINED;
5505
5506   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5507     return;
5508
5509   if (IS_MOVING(x, y))
5510   {
5511     Moving2Blocked(x, y, &newx, &newy);
5512
5513     if (Tile[newx][newy] != EL_BLOCKED)
5514     {
5515       // element is moving, but target field is not free (blocked), but
5516       // already occupied by something different (example: acid pool);
5517       // in this case, only remove the moving field, but not the target
5518
5519       RemoveField(oldx, oldy);
5520
5521       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5522
5523       TEST_DrawLevelField(oldx, oldy);
5524
5525       return;
5526     }
5527   }
5528   else if (element == EL_BLOCKED)
5529   {
5530     Blocked2Moving(x, y, &oldx, &oldy);
5531     if (!IS_MOVING(oldx, oldy))
5532       return;
5533   }
5534
5535   if (element == EL_BLOCKED &&
5536       (Tile[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5537        Tile[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5538        Tile[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5539        Tile[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5540        Tile[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5541        Tile[oldx][oldy] == EL_AMOEBA_DROPPING))
5542     next_element = get_next_element(Tile[oldx][oldy]);
5543
5544   RemoveField(oldx, oldy);
5545   RemoveField(newx, newy);
5546
5547   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5548
5549   if (next_element != EL_UNDEFINED)
5550     Tile[oldx][oldy] = next_element;
5551
5552   TEST_DrawLevelField(oldx, oldy);
5553   TEST_DrawLevelField(newx, newy);
5554 }
5555
5556 void DrawDynamite(int x, int y)
5557 {
5558   int sx = SCREENX(x), sy = SCREENY(y);
5559   int graphic = el2img(Tile[x][y]);
5560   int frame;
5561
5562   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5563     return;
5564
5565   if (IS_WALKABLE_INSIDE(Back[x][y]))
5566     return;
5567
5568   if (Back[x][y])
5569     DrawLevelElement(x, y, Back[x][y]);
5570   else if (Store[x][y])
5571     DrawLevelElement(x, y, Store[x][y]);
5572   else if (game.use_masked_elements)
5573     DrawLevelElement(x, y, EL_EMPTY);
5574
5575   frame = getGraphicAnimationFrameXY(graphic, x, y);
5576
5577   if (Back[x][y] || Store[x][y] || game.use_masked_elements)
5578     DrawGraphicThruMask(sx, sy, graphic, frame);
5579   else
5580     DrawGraphic(sx, sy, graphic, frame);
5581 }
5582
5583 static void CheckDynamite(int x, int y)
5584 {
5585   if (MovDelay[x][y] != 0)      // dynamite is still waiting to explode
5586   {
5587     MovDelay[x][y]--;
5588
5589     if (MovDelay[x][y] != 0)
5590     {
5591       DrawDynamite(x, y);
5592       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5593
5594       return;
5595     }
5596   }
5597
5598   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5599
5600   Bang(x, y);
5601 }
5602
5603 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5604 {
5605   boolean num_checked_players = 0;
5606   int i;
5607
5608   for (i = 0; i < MAX_PLAYERS; i++)
5609   {
5610     if (stored_player[i].active)
5611     {
5612       int sx = stored_player[i].jx;
5613       int sy = stored_player[i].jy;
5614
5615       if (num_checked_players == 0)
5616       {
5617         *sx1 = *sx2 = sx;
5618         *sy1 = *sy2 = sy;
5619       }
5620       else
5621       {
5622         *sx1 = MIN(*sx1, sx);
5623         *sy1 = MIN(*sy1, sy);
5624         *sx2 = MAX(*sx2, sx);
5625         *sy2 = MAX(*sy2, sy);
5626       }
5627
5628       num_checked_players++;
5629     }
5630   }
5631 }
5632
5633 static boolean checkIfAllPlayersFitToScreen_RND(void)
5634 {
5635   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5636
5637   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5638
5639   return (sx2 - sx1 < SCR_FIELDX &&
5640           sy2 - sy1 < SCR_FIELDY);
5641 }
5642
5643 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5644 {
5645   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5646
5647   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5648
5649   *sx = (sx1 + sx2) / 2;
5650   *sy = (sy1 + sy2) / 2;
5651 }
5652
5653 static void DrawRelocateScreen(int old_x, int old_y, int x, int y,
5654                                boolean center_screen, boolean quick_relocation)
5655 {
5656   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5657   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5658   boolean no_delay = (tape.warp_forward);
5659   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5660   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5661   int new_scroll_x, new_scroll_y;
5662
5663   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5664   {
5665     // case 1: quick relocation inside visible screen (without scrolling)
5666
5667     RedrawPlayfield();
5668
5669     return;
5670   }
5671
5672   if (!level.shifted_relocation || center_screen)
5673   {
5674     // relocation _with_ centering of screen
5675
5676     new_scroll_x = SCROLL_POSITION_X(x);
5677     new_scroll_y = SCROLL_POSITION_Y(y);
5678   }
5679   else
5680   {
5681     // relocation _without_ centering of screen
5682
5683     // apply distance between old and new player position to scroll position
5684     int shifted_scroll_x = scroll_x + (x - old_x);
5685     int shifted_scroll_y = scroll_y + (y - old_y);
5686
5687     // make sure that shifted scroll position does not scroll beyond screen
5688     new_scroll_x = SCROLL_POSITION_X(shifted_scroll_x + MIDPOSX);
5689     new_scroll_y = SCROLL_POSITION_Y(shifted_scroll_y + MIDPOSY);
5690   }
5691
5692   if (quick_relocation)
5693   {
5694     // case 2: quick relocation (redraw without visible scrolling)
5695
5696     scroll_x = new_scroll_x;
5697     scroll_y = new_scroll_y;
5698
5699     RedrawPlayfield();
5700
5701     return;
5702   }
5703
5704   // case 3: visible relocation (with scrolling to new position)
5705
5706   ScrollScreen(NULL, SCROLL_GO_ON);     // scroll last frame to full tile
5707
5708   SetVideoFrameDelay(wait_delay_value);
5709
5710   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5711   {
5712     int dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5713     int dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5714
5715     if (dx == 0 && dy == 0)             // no scrolling needed at all
5716       break;
5717
5718     scroll_x -= dx;
5719     scroll_y -= dy;
5720
5721     // set values for horizontal/vertical screen scrolling (half tile size)
5722     int dir_x = (dx != 0 ? MV_HORIZONTAL : 0);
5723     int dir_y = (dy != 0 ? MV_VERTICAL   : 0);
5724     int pos_x = dx * TILEX / 2;
5725     int pos_y = dy * TILEY / 2;
5726     int fx = getFieldbufferOffsetX_RND(dir_x, pos_x);
5727     int fy = getFieldbufferOffsetY_RND(dir_y, pos_y);
5728
5729     ScrollLevel(dx, dy);
5730     DrawAllPlayers();
5731
5732     // scroll in two steps of half tile size to make things smoother
5733     BlitScreenToBitmapExt_RND(window, fx, fy);
5734
5735     // scroll second step to align at full tile size
5736     BlitScreenToBitmap(window);
5737   }
5738
5739   DrawAllPlayers();
5740   BackToFront();
5741
5742   SetVideoFrameDelay(frame_delay_value_old);
5743 }
5744
5745 static void RelocatePlayer(int jx, int jy, int el_player_raw)
5746 {
5747   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5748   int player_nr = GET_PLAYER_NR(el_player);
5749   struct PlayerInfo *player = &stored_player[player_nr];
5750   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5751   boolean no_delay = (tape.warp_forward);
5752   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5753   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5754   int old_jx = player->jx;
5755   int old_jy = player->jy;
5756   int old_element = Tile[old_jx][old_jy];
5757   int element = Tile[jx][jy];
5758   boolean player_relocated = (old_jx != jx || old_jy != jy);
5759
5760   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5761   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5762   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5763   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5764   int leave_side_horiz = move_dir_horiz;
5765   int leave_side_vert  = move_dir_vert;
5766   int enter_side = enter_side_horiz | enter_side_vert;
5767   int leave_side = leave_side_horiz | leave_side_vert;
5768
5769   if (player->buried)           // do not reanimate dead player
5770     return;
5771
5772   if (!player_relocated)        // no need to relocate the player
5773     return;
5774
5775   if (IS_PLAYER(jx, jy))        // player already placed at new position
5776   {
5777     RemoveField(jx, jy);        // temporarily remove newly placed player
5778     DrawLevelField(jx, jy);
5779   }
5780
5781   if (player->present)
5782   {
5783     while (player->MovPos)
5784     {
5785       ScrollPlayer(player, SCROLL_GO_ON);
5786       ScrollScreen(NULL, SCROLL_GO_ON);
5787
5788       AdvanceFrameAndPlayerCounters(player->index_nr);
5789
5790       DrawPlayer(player);
5791
5792       BackToFront_WithFrameDelay(wait_delay_value);
5793     }
5794
5795     DrawPlayer(player);         // needed here only to cleanup last field
5796     DrawLevelField(player->jx, player->jy);     // remove player graphic
5797
5798     player->is_moving = FALSE;
5799   }
5800
5801   if (IS_CUSTOM_ELEMENT(old_element))
5802     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5803                                CE_LEFT_BY_PLAYER,
5804                                player->index_bit, leave_side);
5805
5806   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5807                                       CE_PLAYER_LEAVES_X,
5808                                       player->index_bit, leave_side);
5809
5810   Tile[jx][jy] = el_player;
5811   InitPlayerField(jx, jy, el_player, TRUE);
5812
5813   /* "InitPlayerField()" above sets Tile[jx][jy] to EL_EMPTY, but it may be
5814      possible that the relocation target field did not contain a player element,
5815      but a walkable element, to which the new player was relocated -- in this
5816      case, restore that (already initialized!) element on the player field */
5817   if (!IS_PLAYER_ELEMENT(element))      // player may be set on walkable element
5818   {
5819     Tile[jx][jy] = element;     // restore previously existing element
5820   }
5821
5822   // only visually relocate centered player
5823   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy,
5824                      FALSE, level.instant_relocation);
5825
5826   TestIfPlayerTouchesBadThing(jx, jy);
5827   TestIfPlayerTouchesCustomElement(jx, jy);
5828
5829   if (IS_CUSTOM_ELEMENT(element))
5830     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5831                                player->index_bit, enter_side);
5832
5833   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5834                                       player->index_bit, enter_side);
5835
5836   if (player->is_switching)
5837   {
5838     /* ensure that relocation while still switching an element does not cause
5839        a new element to be treated as also switched directly after relocation
5840        (this is important for teleporter switches that teleport the player to
5841        a place where another teleporter switch is in the same direction, which
5842        would then incorrectly be treated as immediately switched before the
5843        direction key that caused the switch was released) */
5844
5845     player->switch_x += jx - old_jx;
5846     player->switch_y += jy - old_jy;
5847   }
5848 }
5849
5850 static void Explode(int ex, int ey, int phase, int mode)
5851 {
5852   int x, y;
5853   int last_phase;
5854   int border_element;
5855
5856   if (game.explosions_delayed)
5857   {
5858     ExplodeField[ex][ey] = mode;
5859     return;
5860   }
5861
5862   if (phase == EX_PHASE_START)          // initialize 'Store[][]' field
5863   {
5864     int center_element = Tile[ex][ey];
5865     int ce_value = CustomValue[ex][ey];
5866     int ce_score = element_info[center_element].collect_score;
5867     int artwork_element, explosion_element;     // set these values later
5868
5869     // remove things displayed in background while burning dynamite
5870     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5871       Back[ex][ey] = 0;
5872
5873     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5874     {
5875       // put moving element to center field (and let it explode there)
5876       center_element = MovingOrBlocked2Element(ex, ey);
5877       RemoveMovingField(ex, ey);
5878       Tile[ex][ey] = center_element;
5879     }
5880
5881     // now "center_element" is finally determined -- set related values now
5882     artwork_element = center_element;           // for custom player artwork
5883     explosion_element = center_element;         // for custom player artwork
5884
5885     if (IS_PLAYER(ex, ey))
5886     {
5887       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5888
5889       artwork_element = stored_player[player_nr].artwork_element;
5890
5891       if (level.use_explosion_element[player_nr])
5892       {
5893         explosion_element = level.explosion_element[player_nr];
5894         artwork_element = explosion_element;
5895       }
5896     }
5897
5898     if (mode == EX_TYPE_NORMAL ||
5899         mode == EX_TYPE_CENTER ||
5900         mode == EX_TYPE_CROSS)
5901       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5902
5903     last_phase = element_info[explosion_element].explosion_delay + 1;
5904
5905     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5906     {
5907       int xx = x - ex + 1;
5908       int yy = y - ey + 1;
5909       int element;
5910
5911       if (!IN_LEV_FIELD(x, y) ||
5912           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5913           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5914         continue;
5915
5916       element = Tile[x][y];
5917
5918       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5919       {
5920         element = MovingOrBlocked2Element(x, y);
5921
5922         if (!IS_EXPLOSION_PROOF(element))
5923           RemoveMovingField(x, y);
5924       }
5925
5926       // indestructible elements can only explode in center (but not flames)
5927       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5928                                            mode == EX_TYPE_BORDER)) ||
5929           element == EL_FLAMES)
5930         continue;
5931
5932       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5933          behaviour, for example when touching a yamyam that explodes to rocks
5934          with active deadly shield, a rock is created under the player !!! */
5935       // (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8)
5936 #if 0
5937       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5938           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5939            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5940 #else
5941       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5942 #endif
5943       {
5944         if (IS_ACTIVE_BOMB(element))
5945         {
5946           // re-activate things under the bomb like gate or penguin
5947           Tile[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5948           Back[x][y] = 0;
5949         }
5950
5951         continue;
5952       }
5953
5954       // save walkable background elements while explosion on same tile
5955       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5956           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5957         Back[x][y] = element;
5958
5959       // ignite explodable elements reached by other explosion
5960       if (element == EL_EXPLOSION)
5961         element = Store2[x][y];
5962
5963       if (AmoebaNr[x][y] &&
5964           (element == EL_AMOEBA_FULL ||
5965            element == EL_BD_AMOEBA ||
5966            element == EL_AMOEBA_GROWING))
5967       {
5968         AmoebaCnt[AmoebaNr[x][y]]--;
5969         AmoebaCnt2[AmoebaNr[x][y]]--;
5970       }
5971
5972       RemoveField(x, y);
5973
5974       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5975       {
5976         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5977
5978         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5979
5980         if (PLAYERINFO(ex, ey)->use_murphy)
5981           Store[x][y] = EL_EMPTY;
5982       }
5983
5984       // !!! check this case -- currently needed for rnd_rado_negundo_v,
5985       // !!! levels 015 018 019 020 021 022 023 026 027 028 !!!
5986       else if (IS_PLAYER_ELEMENT(center_element))
5987         Store[x][y] = EL_EMPTY;
5988       else if (center_element == EL_YAMYAM)
5989         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5990       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5991         Store[x][y] = element_info[center_element].content.e[xx][yy];
5992 #if 1
5993       // needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5994       // (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5995       // otherwise) -- FIX THIS !!!
5996       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5997         Store[x][y] = element_info[element].content.e[1][1];
5998 #else
5999       else if (!CAN_EXPLODE(element))
6000         Store[x][y] = element_info[element].content.e[1][1];
6001 #endif
6002       else
6003         Store[x][y] = EL_EMPTY;
6004
6005       if (IS_CUSTOM_ELEMENT(center_element))
6006         Store[x][y] = (Store[x][y] == EL_CURRENT_CE_VALUE ? ce_value :
6007                        Store[x][y] == EL_CURRENT_CE_SCORE ? ce_score :
6008                        Store[x][y] >= EL_PREV_CE_8 &&
6009                        Store[x][y] <= EL_NEXT_CE_8 ?
6010                        RESOLVED_REFERENCE_ELEMENT(center_element, Store[x][y]) :
6011                        Store[x][y]);
6012
6013       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
6014           center_element == EL_AMOEBA_TO_DIAMOND)
6015         Store2[x][y] = element;
6016
6017       Tile[x][y] = EL_EXPLOSION;
6018       GfxElement[x][y] = artwork_element;
6019
6020       ExplodePhase[x][y] = 1;
6021       ExplodeDelay[x][y] = last_phase;
6022
6023       Stop[x][y] = TRUE;
6024     }
6025
6026     if (center_element == EL_YAMYAM)
6027       game.yamyam_content_nr =
6028         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
6029
6030     return;
6031   }
6032
6033   if (Stop[ex][ey])
6034     return;
6035
6036   x = ex;
6037   y = ey;
6038
6039   if (phase == 1)
6040     GfxFrame[x][y] = 0;         // restart explosion animation
6041
6042   last_phase = ExplodeDelay[x][y];
6043
6044   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
6045
6046   // this can happen if the player leaves an explosion just in time
6047   if (GfxElement[x][y] == EL_UNDEFINED)
6048     GfxElement[x][y] = EL_EMPTY;
6049
6050   border_element = Store2[x][y];
6051   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6052     border_element = StorePlayer[x][y];
6053
6054   if (phase == element_info[border_element].ignition_delay ||
6055       phase == last_phase)
6056   {
6057     boolean border_explosion = FALSE;
6058
6059     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
6060         !PLAYER_EXPLOSION_PROTECTED(x, y))
6061     {
6062       KillPlayerUnlessExplosionProtected(x, y);
6063       border_explosion = TRUE;
6064     }
6065     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
6066     {
6067       Tile[x][y] = Store2[x][y];
6068       Store2[x][y] = 0;
6069       Bang(x, y);
6070       border_explosion = TRUE;
6071     }
6072     else if (border_element == EL_AMOEBA_TO_DIAMOND)
6073     {
6074       AmoebaToDiamond(x, y);
6075       Store2[x][y] = 0;
6076       border_explosion = TRUE;
6077     }
6078
6079     // if an element just explodes due to another explosion (chain-reaction),
6080     // do not immediately end the new explosion when it was the last frame of
6081     // the explosion (as it would be done in the following "if"-statement!)
6082     if (border_explosion && phase == last_phase)
6083       return;
6084   }
6085
6086   // this can happen if the player was just killed by an explosion
6087   if (GfxElement[x][y] == EL_UNDEFINED)
6088     GfxElement[x][y] = EL_EMPTY;
6089
6090   if (phase == last_phase)
6091   {
6092     int element;
6093
6094     element = Tile[x][y] = Store[x][y];
6095     Store[x][y] = Store2[x][y] = 0;
6096     GfxElement[x][y] = EL_UNDEFINED;
6097
6098     // player can escape from explosions and might therefore be still alive
6099     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
6100         element <= EL_PLAYER_IS_EXPLODING_4)
6101     {
6102       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
6103       int explosion_element = EL_PLAYER_1 + player_nr;
6104       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
6105       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
6106
6107       if (level.use_explosion_element[player_nr])
6108         explosion_element = level.explosion_element[player_nr];
6109
6110       Tile[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
6111                     element_info[explosion_element].content.e[xx][yy]);
6112     }
6113
6114     // restore probably existing indestructible background element
6115     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
6116       element = Tile[x][y] = Back[x][y];
6117     Back[x][y] = 0;
6118
6119     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
6120     GfxDir[x][y] = MV_NONE;
6121     ChangeDelay[x][y] = 0;
6122     ChangePage[x][y] = -1;
6123
6124     CustomValue[x][y] = 0;
6125
6126     InitField_WithBug2(x, y, FALSE);
6127
6128     TEST_DrawLevelField(x, y);
6129
6130     TestIfElementTouchesCustomElement(x, y);
6131
6132     if (GFX_CRUMBLED(element))
6133       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6134
6135     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
6136       StorePlayer[x][y] = 0;
6137
6138     if (IS_PLAYER_ELEMENT(element))
6139       RelocatePlayer(x, y, element);
6140   }
6141   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6142   {
6143     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
6144     int frame = getGraphicAnimationFrameXY(graphic, x, y);
6145
6146     if (phase == 1)
6147       TEST_DrawLevelFieldCrumbled(x, y);
6148
6149     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
6150     {
6151       DrawLevelElement(x, y, Back[x][y]);
6152       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
6153     }
6154     else if (IS_WALKABLE_UNDER(Back[x][y]))
6155     {
6156       DrawLevelGraphic(x, y, graphic, frame);
6157       DrawLevelElementThruMask(x, y, Back[x][y]);
6158     }
6159     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
6160       DrawLevelGraphic(x, y, graphic, frame);
6161   }
6162 }
6163
6164 static void DynaExplode(int ex, int ey)
6165 {
6166   int i, j;
6167   int dynabomb_element = Tile[ex][ey];
6168   int dynabomb_size = 1;
6169   boolean dynabomb_xl = FALSE;
6170   struct PlayerInfo *player;
6171   struct XY *xy = xy_topdown;
6172
6173   if (IS_ACTIVE_BOMB(dynabomb_element))
6174   {
6175     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
6176     dynabomb_size = player->dynabomb_size;
6177     dynabomb_xl = player->dynabomb_xl;
6178     player->dynabombs_left++;
6179   }
6180
6181   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6182
6183   for (i = 0; i < NUM_DIRECTIONS; i++)
6184   {
6185     for (j = 1; j <= dynabomb_size; j++)
6186     {
6187       int x = ex + j * xy[i].x;
6188       int y = ey + j * xy[i].y;
6189       int element;
6190
6191       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Tile[x][y]))
6192         break;
6193
6194       element = Tile[x][y];
6195
6196       // do not restart explosions of fields with active bombs
6197       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6198         continue;
6199
6200       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6201
6202       if (element != EL_EMPTY && element != EL_EXPLOSION &&
6203           !IS_DIGGABLE(element) && !dynabomb_xl)
6204         break;
6205     }
6206   }
6207 }
6208
6209 void Bang(int x, int y)
6210 {
6211   int element = MovingOrBlocked2Element(x, y);
6212   int explosion_type = EX_TYPE_NORMAL;
6213
6214   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6215   {
6216     struct PlayerInfo *player = PLAYERINFO(x, y);
6217
6218     element = Tile[x][y] = player->initial_element;
6219
6220     if (level.use_explosion_element[player->index_nr])
6221     {
6222       int explosion_element = level.explosion_element[player->index_nr];
6223
6224       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6225         explosion_type = EX_TYPE_CROSS;
6226       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6227         explosion_type = EX_TYPE_CENTER;
6228     }
6229   }
6230
6231   switch (element)
6232   {
6233     case EL_BUG:
6234     case EL_SPACESHIP:
6235     case EL_BD_BUTTERFLY:
6236     case EL_BD_FIREFLY:
6237     case EL_YAMYAM:
6238     case EL_DARK_YAMYAM:
6239     case EL_ROBOT:
6240     case EL_PACMAN:
6241     case EL_MOLE:
6242       RaiseScoreElement(element);
6243       break;
6244
6245     case EL_DYNABOMB_PLAYER_1_ACTIVE:
6246     case EL_DYNABOMB_PLAYER_2_ACTIVE:
6247     case EL_DYNABOMB_PLAYER_3_ACTIVE:
6248     case EL_DYNABOMB_PLAYER_4_ACTIVE:
6249     case EL_DYNABOMB_INCREASE_NUMBER:
6250     case EL_DYNABOMB_INCREASE_SIZE:
6251     case EL_DYNABOMB_INCREASE_POWER:
6252       explosion_type = EX_TYPE_DYNA;
6253       break;
6254
6255     case EL_DC_LANDMINE:
6256       explosion_type = EX_TYPE_CENTER;
6257       break;
6258
6259     case EL_PENGUIN:
6260     case EL_LAMP:
6261     case EL_LAMP_ACTIVE:
6262     case EL_AMOEBA_TO_DIAMOND:
6263       if (!IS_PLAYER(x, y))     // penguin and player may be at same field
6264         explosion_type = EX_TYPE_CENTER;
6265       break;
6266
6267     default:
6268       if (element_info[element].explosion_type == EXPLODES_CROSS)
6269         explosion_type = EX_TYPE_CROSS;
6270       else if (element_info[element].explosion_type == EXPLODES_1X1)
6271         explosion_type = EX_TYPE_CENTER;
6272       break;
6273   }
6274
6275   if (explosion_type == EX_TYPE_DYNA)
6276     DynaExplode(x, y);
6277   else
6278     Explode(x, y, EX_PHASE_START, explosion_type);
6279
6280   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6281 }
6282
6283 static void SplashAcid(int x, int y)
6284 {
6285   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6286       (!IN_LEV_FIELD(x - 1, y - 2) ||
6287        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6288     Tile[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6289
6290   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6291       (!IN_LEV_FIELD(x + 1, y - 2) ||
6292        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6293     Tile[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6294
6295   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6296 }
6297
6298 static void InitBeltMovement(void)
6299 {
6300   static int belt_base_element[4] =
6301   {
6302     EL_CONVEYOR_BELT_1_LEFT,
6303     EL_CONVEYOR_BELT_2_LEFT,
6304     EL_CONVEYOR_BELT_3_LEFT,
6305     EL_CONVEYOR_BELT_4_LEFT
6306   };
6307   static int belt_base_active_element[4] =
6308   {
6309     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6310     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6311     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6312     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6313   };
6314
6315   int x, y, i, j;
6316
6317   // set frame order for belt animation graphic according to belt direction
6318   for (i = 0; i < NUM_BELTS; i++)
6319   {
6320     int belt_nr = i;
6321
6322     for (j = 0; j < NUM_BELT_PARTS; j++)
6323     {
6324       int element = belt_base_active_element[belt_nr] + j;
6325       int graphic_1 = el2img(element);
6326       int graphic_2 = el2panelimg(element);
6327
6328       if (game.belt_dir[i] == MV_LEFT)
6329       {
6330         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6331         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6332       }
6333       else
6334       {
6335         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6336         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6337       }
6338     }
6339   }
6340
6341   SCAN_PLAYFIELD(x, y)
6342   {
6343     int element = Tile[x][y];
6344
6345     for (i = 0; i < NUM_BELTS; i++)
6346     {
6347       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6348       {
6349         int e_belt_nr = getBeltNrFromBeltElement(element);
6350         int belt_nr = i;
6351
6352         if (e_belt_nr == belt_nr)
6353         {
6354           int belt_part = Tile[x][y] - belt_base_element[belt_nr];
6355
6356           Tile[x][y] = belt_base_active_element[belt_nr] + belt_part;
6357         }
6358       }
6359     }
6360   }
6361 }
6362
6363 static void ToggleBeltSwitch(int x, int y)
6364 {
6365   static int belt_base_element[4] =
6366   {
6367     EL_CONVEYOR_BELT_1_LEFT,
6368     EL_CONVEYOR_BELT_2_LEFT,
6369     EL_CONVEYOR_BELT_3_LEFT,
6370     EL_CONVEYOR_BELT_4_LEFT
6371   };
6372   static int belt_base_active_element[4] =
6373   {
6374     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6375     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6376     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6377     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6378   };
6379   static int belt_base_switch_element[4] =
6380   {
6381     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6382     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6383     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6384     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6385   };
6386   static int belt_move_dir[4] =
6387   {
6388     MV_LEFT,
6389     MV_NONE,
6390     MV_RIGHT,
6391     MV_NONE,
6392   };
6393
6394   int element = Tile[x][y];
6395   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6396   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6397   int belt_dir = belt_move_dir[belt_dir_nr];
6398   int xx, yy, i;
6399
6400   if (!IS_BELT_SWITCH(element))
6401     return;
6402
6403   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6404   game.belt_dir[belt_nr] = belt_dir;
6405
6406   if (belt_dir_nr == 3)
6407     belt_dir_nr = 1;
6408
6409   // set frame order for belt animation graphic according to belt direction
6410   for (i = 0; i < NUM_BELT_PARTS; i++)
6411   {
6412     int element = belt_base_active_element[belt_nr] + i;
6413     int graphic_1 = el2img(element);
6414     int graphic_2 = el2panelimg(element);
6415
6416     if (belt_dir == MV_LEFT)
6417     {
6418       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6419       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6420     }
6421     else
6422     {
6423       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6424       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6425     }
6426   }
6427
6428   SCAN_PLAYFIELD(xx, yy)
6429   {
6430     int element = Tile[xx][yy];
6431
6432     if (IS_BELT_SWITCH(element))
6433     {
6434       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6435
6436       if (e_belt_nr == belt_nr)
6437       {
6438         Tile[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6439         TEST_DrawLevelField(xx, yy);
6440       }
6441     }
6442     else if (IS_BELT(element) && belt_dir != MV_NONE)
6443     {
6444       int e_belt_nr = getBeltNrFromBeltElement(element);
6445
6446       if (e_belt_nr == belt_nr)
6447       {
6448         int belt_part = Tile[xx][yy] - belt_base_element[belt_nr];
6449
6450         Tile[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6451         TEST_DrawLevelField(xx, yy);
6452       }
6453     }
6454     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6455     {
6456       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6457
6458       if (e_belt_nr == belt_nr)
6459       {
6460         int belt_part = Tile[xx][yy] - belt_base_active_element[belt_nr];
6461
6462         Tile[xx][yy] = belt_base_element[belt_nr] + belt_part;
6463         TEST_DrawLevelField(xx, yy);
6464       }
6465     }
6466   }
6467 }
6468
6469 static void ToggleSwitchgateSwitch(void)
6470 {
6471   int xx, yy;
6472
6473   game.switchgate_pos = !game.switchgate_pos;
6474
6475   SCAN_PLAYFIELD(xx, yy)
6476   {
6477     int element = Tile[xx][yy];
6478
6479     if (element == EL_SWITCHGATE_SWITCH_UP)
6480     {
6481       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6482       TEST_DrawLevelField(xx, yy);
6483     }
6484     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6485     {
6486       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6487       TEST_DrawLevelField(xx, yy);
6488     }
6489     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6490     {
6491       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6492       TEST_DrawLevelField(xx, yy);
6493     }
6494     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6495     {
6496       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6497       TEST_DrawLevelField(xx, yy);
6498     }
6499     else if (element == EL_SWITCHGATE_OPEN ||
6500              element == EL_SWITCHGATE_OPENING)
6501     {
6502       Tile[xx][yy] = EL_SWITCHGATE_CLOSING;
6503
6504       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6505     }
6506     else if (element == EL_SWITCHGATE_CLOSED ||
6507              element == EL_SWITCHGATE_CLOSING)
6508     {
6509       Tile[xx][yy] = EL_SWITCHGATE_OPENING;
6510
6511       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6512     }
6513   }
6514 }
6515
6516 static int getInvisibleActiveFromInvisibleElement(int element)
6517 {
6518   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6519           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6520           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6521           element);
6522 }
6523
6524 static int getInvisibleFromInvisibleActiveElement(int element)
6525 {
6526   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6527           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6528           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6529           element);
6530 }
6531
6532 static void RedrawAllLightSwitchesAndInvisibleElements(void)
6533 {
6534   int x, y;
6535
6536   SCAN_PLAYFIELD(x, y)
6537   {
6538     int element = Tile[x][y];
6539
6540     if (element == EL_LIGHT_SWITCH &&
6541         game.light_time_left > 0)
6542     {
6543       Tile[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6544       TEST_DrawLevelField(x, y);
6545     }
6546     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6547              game.light_time_left == 0)
6548     {
6549       Tile[x][y] = EL_LIGHT_SWITCH;
6550       TEST_DrawLevelField(x, y);
6551     }
6552     else if (element == EL_EMC_DRIPPER &&
6553              game.light_time_left > 0)
6554     {
6555       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6556       TEST_DrawLevelField(x, y);
6557     }
6558     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6559              game.light_time_left == 0)
6560     {
6561       Tile[x][y] = EL_EMC_DRIPPER;
6562       TEST_DrawLevelField(x, y);
6563     }
6564     else if (element == EL_INVISIBLE_STEELWALL ||
6565              element == EL_INVISIBLE_WALL ||
6566              element == EL_INVISIBLE_SAND)
6567     {
6568       if (game.light_time_left > 0)
6569         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6570
6571       TEST_DrawLevelField(x, y);
6572
6573       // uncrumble neighbour fields, if needed
6574       if (element == EL_INVISIBLE_SAND)
6575         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6576     }
6577     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6578              element == EL_INVISIBLE_WALL_ACTIVE ||
6579              element == EL_INVISIBLE_SAND_ACTIVE)
6580     {
6581       if (game.light_time_left == 0)
6582         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6583
6584       TEST_DrawLevelField(x, y);
6585
6586       // re-crumble neighbour fields, if needed
6587       if (element == EL_INVISIBLE_SAND)
6588         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6589     }
6590   }
6591 }
6592
6593 static void RedrawAllInvisibleElementsForLenses(void)
6594 {
6595   int x, y;
6596
6597   SCAN_PLAYFIELD(x, y)
6598   {
6599     int element = Tile[x][y];
6600
6601     if (element == EL_EMC_DRIPPER &&
6602         game.lenses_time_left > 0)
6603     {
6604       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6605       TEST_DrawLevelField(x, y);
6606     }
6607     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6608              game.lenses_time_left == 0)
6609     {
6610       Tile[x][y] = EL_EMC_DRIPPER;
6611       TEST_DrawLevelField(x, y);
6612     }
6613     else if (element == EL_INVISIBLE_STEELWALL ||
6614              element == EL_INVISIBLE_WALL ||
6615              element == EL_INVISIBLE_SAND)
6616     {
6617       if (game.lenses_time_left > 0)
6618         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6619
6620       TEST_DrawLevelField(x, y);
6621
6622       // uncrumble neighbour fields, if needed
6623       if (element == EL_INVISIBLE_SAND)
6624         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6625     }
6626     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6627              element == EL_INVISIBLE_WALL_ACTIVE ||
6628              element == EL_INVISIBLE_SAND_ACTIVE)
6629     {
6630       if (game.lenses_time_left == 0)
6631         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6632
6633       TEST_DrawLevelField(x, y);
6634
6635       // re-crumble neighbour fields, if needed
6636       if (element == EL_INVISIBLE_SAND)
6637         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6638     }
6639   }
6640 }
6641
6642 static void RedrawAllInvisibleElementsForMagnifier(void)
6643 {
6644   int x, y;
6645
6646   SCAN_PLAYFIELD(x, y)
6647   {
6648     int element = Tile[x][y];
6649
6650     if (element == EL_EMC_FAKE_GRASS &&
6651         game.magnify_time_left > 0)
6652     {
6653       Tile[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6654       TEST_DrawLevelField(x, y);
6655     }
6656     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6657              game.magnify_time_left == 0)
6658     {
6659       Tile[x][y] = EL_EMC_FAKE_GRASS;
6660       TEST_DrawLevelField(x, y);
6661     }
6662     else if (IS_GATE_GRAY(element) &&
6663              game.magnify_time_left > 0)
6664     {
6665       Tile[x][y] = (IS_RND_GATE_GRAY(element) ?
6666                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6667                     IS_EM_GATE_GRAY(element) ?
6668                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6669                     IS_EMC_GATE_GRAY(element) ?
6670                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6671                     IS_DC_GATE_GRAY(element) ?
6672                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6673                     element);
6674       TEST_DrawLevelField(x, y);
6675     }
6676     else if (IS_GATE_GRAY_ACTIVE(element) &&
6677              game.magnify_time_left == 0)
6678     {
6679       Tile[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6680                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6681                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6682                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6683                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6684                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6685                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6686                     EL_DC_GATE_WHITE_GRAY :
6687                     element);
6688       TEST_DrawLevelField(x, y);
6689     }
6690   }
6691 }
6692
6693 static void ToggleLightSwitch(int x, int y)
6694 {
6695   int element = Tile[x][y];
6696
6697   game.light_time_left =
6698     (element == EL_LIGHT_SWITCH ?
6699      level.time_light * FRAMES_PER_SECOND : 0);
6700
6701   RedrawAllLightSwitchesAndInvisibleElements();
6702 }
6703
6704 static void ActivateTimegateSwitch(int x, int y)
6705 {
6706   int xx, yy;
6707
6708   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6709
6710   SCAN_PLAYFIELD(xx, yy)
6711   {
6712     int element = Tile[xx][yy];
6713
6714     if (element == EL_TIMEGATE_CLOSED ||
6715         element == EL_TIMEGATE_CLOSING)
6716     {
6717       Tile[xx][yy] = EL_TIMEGATE_OPENING;
6718       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6719     }
6720
6721     /*
6722     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6723     {
6724       Tile[xx][yy] = EL_TIMEGATE_SWITCH;
6725       TEST_DrawLevelField(xx, yy);
6726     }
6727     */
6728
6729   }
6730
6731   Tile[x][y] = (Tile[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6732                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6733 }
6734
6735 static void Impact(int x, int y)
6736 {
6737   boolean last_line = (y == lev_fieldy - 1);
6738   boolean object_hit = FALSE;
6739   boolean impact = (last_line || object_hit);
6740   int element = Tile[x][y];
6741   int smashed = EL_STEELWALL;
6742
6743   if (!last_line)       // check if element below was hit
6744   {
6745     if (Tile[x][y + 1] == EL_PLAYER_IS_LEAVING)
6746       return;
6747
6748     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6749                                          MovDir[x][y + 1] != MV_DOWN ||
6750                                          MovPos[x][y + 1] <= TILEY / 2));
6751
6752     // do not smash moving elements that left the smashed field in time
6753     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6754         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6755       object_hit = FALSE;
6756
6757 #if USE_QUICKSAND_IMPACT_BUGFIX
6758     if (Tile[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6759     {
6760       RemoveMovingField(x, y + 1);
6761       Tile[x][y + 1] = EL_QUICKSAND_EMPTY;
6762       Tile[x][y + 2] = EL_ROCK;
6763       TEST_DrawLevelField(x, y + 2);
6764
6765       object_hit = TRUE;
6766     }
6767
6768     if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6769     {
6770       RemoveMovingField(x, y + 1);
6771       Tile[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6772       Tile[x][y + 2] = EL_ROCK;
6773       TEST_DrawLevelField(x, y + 2);
6774
6775       object_hit = TRUE;
6776     }
6777 #endif
6778
6779     if (object_hit)
6780       smashed = MovingOrBlocked2Element(x, y + 1);
6781
6782     impact = (last_line || object_hit);
6783   }
6784
6785   if (!last_line && smashed == EL_ACID) // element falls into acid
6786   {
6787     SplashAcid(x, y + 1);
6788     return;
6789   }
6790
6791   // !!! not sufficient for all cases -- see EL_PEARL below !!!
6792   // only reset graphic animation if graphic really changes after impact
6793   if (impact &&
6794       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6795   {
6796     ResetGfxAnimation(x, y);
6797     TEST_DrawLevelField(x, y);
6798   }
6799
6800   if (impact && CAN_EXPLODE_IMPACT(element))
6801   {
6802     Bang(x, y);
6803     return;
6804   }
6805   else if (impact && element == EL_PEARL &&
6806            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6807   {
6808     ResetGfxAnimation(x, y);
6809
6810     Tile[x][y] = EL_PEARL_BREAKING;
6811     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6812     return;
6813   }
6814   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6815   {
6816     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6817
6818     return;
6819   }
6820
6821   if (impact && element == EL_AMOEBA_DROP)
6822   {
6823     if (object_hit && IS_PLAYER(x, y + 1))
6824       KillPlayerUnlessEnemyProtected(x, y + 1);
6825     else if (object_hit && smashed == EL_PENGUIN)
6826       Bang(x, y + 1);
6827     else
6828     {
6829       Tile[x][y] = EL_AMOEBA_GROWING;
6830       Store[x][y] = EL_AMOEBA_WET;
6831
6832       ResetRandomAnimationValue(x, y);
6833     }
6834     return;
6835   }
6836
6837   if (object_hit)               // check which object was hit
6838   {
6839     if ((CAN_PASS_MAGIC_WALL(element) && 
6840          (smashed == EL_MAGIC_WALL ||
6841           smashed == EL_BD_MAGIC_WALL)) ||
6842         (CAN_PASS_DC_MAGIC_WALL(element) &&
6843          smashed == EL_DC_MAGIC_WALL))
6844     {
6845       int xx, yy;
6846       int activated_magic_wall =
6847         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6848          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6849          EL_DC_MAGIC_WALL_ACTIVE);
6850
6851       // activate magic wall / mill
6852       SCAN_PLAYFIELD(xx, yy)
6853       {
6854         if (Tile[xx][yy] == smashed)
6855           Tile[xx][yy] = activated_magic_wall;
6856       }
6857
6858       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6859       game.magic_wall_active = TRUE;
6860
6861       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6862                             SND_MAGIC_WALL_ACTIVATING :
6863                             smashed == EL_BD_MAGIC_WALL ?
6864                             SND_BD_MAGIC_WALL_ACTIVATING :
6865                             SND_DC_MAGIC_WALL_ACTIVATING));
6866     }
6867
6868     if (IS_PLAYER(x, y + 1))
6869     {
6870       if (CAN_SMASH_PLAYER(element))
6871       {
6872         KillPlayerUnlessEnemyProtected(x, y + 1);
6873         return;
6874       }
6875     }
6876     else if (smashed == EL_PENGUIN)
6877     {
6878       if (CAN_SMASH_PLAYER(element))
6879       {
6880         Bang(x, y + 1);
6881         return;
6882       }
6883     }
6884     else if (element == EL_BD_DIAMOND)
6885     {
6886       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6887       {
6888         Bang(x, y + 1);
6889         return;
6890       }
6891     }
6892     else if (((element == EL_SP_INFOTRON ||
6893                element == EL_SP_ZONK) &&
6894               (smashed == EL_SP_SNIKSNAK ||
6895                smashed == EL_SP_ELECTRON ||
6896                smashed == EL_SP_DISK_ORANGE)) ||
6897              (element == EL_SP_INFOTRON &&
6898               smashed == EL_SP_DISK_YELLOW))
6899     {
6900       Bang(x, y + 1);
6901       return;
6902     }
6903     else if (CAN_SMASH_EVERYTHING(element))
6904     {
6905       if (IS_CLASSIC_ENEMY(smashed) ||
6906           CAN_EXPLODE_SMASHED(smashed))
6907       {
6908         Bang(x, y + 1);
6909         return;
6910       }
6911       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6912       {
6913         if (smashed == EL_LAMP ||
6914             smashed == EL_LAMP_ACTIVE)
6915         {
6916           Bang(x, y + 1);
6917           return;
6918         }
6919         else if (smashed == EL_NUT)
6920         {
6921           Tile[x][y + 1] = EL_NUT_BREAKING;
6922           PlayLevelSound(x, y, SND_NUT_BREAKING);
6923           RaiseScoreElement(EL_NUT);
6924           return;
6925         }
6926         else if (smashed == EL_PEARL)
6927         {
6928           ResetGfxAnimation(x, y);
6929
6930           Tile[x][y + 1] = EL_PEARL_BREAKING;
6931           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6932           return;
6933         }
6934         else if (smashed == EL_DIAMOND)
6935         {
6936           Tile[x][y + 1] = EL_DIAMOND_BREAKING;
6937           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6938           return;
6939         }
6940         else if (IS_BELT_SWITCH(smashed))
6941         {
6942           ToggleBeltSwitch(x, y + 1);
6943         }
6944         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6945                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6946                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6947                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6948         {
6949           ToggleSwitchgateSwitch();
6950         }
6951         else if (smashed == EL_LIGHT_SWITCH ||
6952                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6953         {
6954           ToggleLightSwitch(x, y + 1);
6955         }
6956         else
6957         {
6958           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6959
6960           CheckElementChangeBySide(x, y + 1, smashed, element,
6961                                    CE_SWITCHED, CH_SIDE_TOP);
6962           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6963                                             CH_SIDE_TOP);
6964         }
6965       }
6966       else
6967       {
6968         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6969       }
6970     }
6971   }
6972
6973   // play sound of magic wall / mill
6974   if (!last_line &&
6975       (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6976        Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6977        Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6978   {
6979     if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6980       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6981     else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6982       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6983     else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6984       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6985
6986     return;
6987   }
6988
6989   // play sound of object that hits the ground
6990   if (last_line || object_hit)
6991     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6992 }
6993
6994 static void TurnRoundExt(int x, int y)
6995 {
6996   static struct
6997   {
6998     int dx, dy;
6999   } move_xy[] =
7000   {
7001     {  0,  0 },
7002     { -1,  0 },
7003     { +1,  0 },
7004     {  0,  0 },
7005     {  0, -1 },
7006     {  0,  0 }, { 0, 0 }, { 0, 0 },
7007     {  0, +1 }
7008   };
7009   static struct
7010   {
7011     int left, right, back;
7012   } turn[] =
7013   {
7014     { 0,        0,              0        },
7015     { MV_DOWN,  MV_UP,          MV_RIGHT },
7016     { MV_UP,    MV_DOWN,        MV_LEFT  },
7017     { 0,        0,              0        },
7018     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
7019     { 0,        0,              0        },
7020     { 0,        0,              0        },
7021     { 0,        0,              0        },
7022     { MV_RIGHT, MV_LEFT,        MV_UP    }
7023   };
7024
7025   int element = Tile[x][y];
7026   int move_pattern = element_info[element].move_pattern;
7027
7028   int old_move_dir = MovDir[x][y];
7029   int left_dir  = turn[old_move_dir].left;
7030   int right_dir = turn[old_move_dir].right;
7031   int back_dir  = turn[old_move_dir].back;
7032
7033   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
7034   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
7035   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
7036   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
7037
7038   int left_x  = x + left_dx,  left_y  = y + left_dy;
7039   int right_x = x + right_dx, right_y = y + right_dy;
7040   int move_x  = x + move_dx,  move_y  = y + move_dy;
7041
7042   int xx, yy;
7043
7044   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
7045   {
7046     TestIfBadThingTouchesOtherBadThing(x, y);
7047
7048     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
7049       MovDir[x][y] = right_dir;
7050     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7051       MovDir[x][y] = left_dir;
7052
7053     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
7054       MovDelay[x][y] = 9;
7055     else if (element == EL_BD_BUTTERFLY)     // && MovDir[x][y] == left_dir)
7056       MovDelay[x][y] = 1;
7057   }
7058   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
7059   {
7060     TestIfBadThingTouchesOtherBadThing(x, y);
7061
7062     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
7063       MovDir[x][y] = left_dir;
7064     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7065       MovDir[x][y] = right_dir;
7066
7067     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
7068       MovDelay[x][y] = 9;
7069     else if (element == EL_BD_FIREFLY)      // && MovDir[x][y] == right_dir)
7070       MovDelay[x][y] = 1;
7071   }
7072   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
7073   {
7074     TestIfBadThingTouchesOtherBadThing(x, y);
7075
7076     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
7077       MovDir[x][y] = left_dir;
7078     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
7079       MovDir[x][y] = right_dir;
7080
7081     if (MovDir[x][y] != old_move_dir)
7082       MovDelay[x][y] = 9;
7083   }
7084   else if (element == EL_YAMYAM)
7085   {
7086     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
7087     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
7088
7089     if (can_turn_left && can_turn_right)
7090       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7091     else if (can_turn_left)
7092       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7093     else if (can_turn_right)
7094       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7095     else
7096       MovDir[x][y] = back_dir;
7097
7098     MovDelay[x][y] = 16 + 16 * RND(3);
7099   }
7100   else if (element == EL_DARK_YAMYAM)
7101   {
7102     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7103                                                          left_x, left_y);
7104     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7105                                                          right_x, right_y);
7106
7107     if (can_turn_left && can_turn_right)
7108       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7109     else if (can_turn_left)
7110       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7111     else if (can_turn_right)
7112       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7113     else
7114       MovDir[x][y] = back_dir;
7115
7116     MovDelay[x][y] = 16 + 16 * RND(3);
7117   }
7118   else if (element == EL_PACMAN)
7119   {
7120     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
7121     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
7122
7123     if (can_turn_left && can_turn_right)
7124       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7125     else if (can_turn_left)
7126       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7127     else if (can_turn_right)
7128       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7129     else
7130       MovDir[x][y] = back_dir;
7131
7132     MovDelay[x][y] = 6 + RND(40);
7133   }
7134   else if (element == EL_PIG)
7135   {
7136     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
7137     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
7138     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
7139     boolean should_turn_left, should_turn_right, should_move_on;
7140     int rnd_value = 24;
7141     int rnd = RND(rnd_value);
7142
7143     should_turn_left = (can_turn_left &&
7144                         (!can_move_on ||
7145                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
7146                                                    y + back_dy + left_dy)));
7147     should_turn_right = (can_turn_right &&
7148                          (!can_move_on ||
7149                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
7150                                                     y + back_dy + right_dy)));
7151     should_move_on = (can_move_on &&
7152                       (!can_turn_left ||
7153                        !can_turn_right ||
7154                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
7155                                                  y + move_dy + left_dy) ||
7156                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
7157                                                  y + move_dy + right_dy)));
7158
7159     if (should_turn_left || should_turn_right || should_move_on)
7160     {
7161       if (should_turn_left && should_turn_right && should_move_on)
7162         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
7163                         rnd < 2 * rnd_value / 3 ? right_dir :
7164                         old_move_dir);
7165       else if (should_turn_left && should_turn_right)
7166         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7167       else if (should_turn_left && should_move_on)
7168         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
7169       else if (should_turn_right && should_move_on)
7170         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
7171       else if (should_turn_left)
7172         MovDir[x][y] = left_dir;
7173       else if (should_turn_right)
7174         MovDir[x][y] = right_dir;
7175       else if (should_move_on)
7176         MovDir[x][y] = old_move_dir;
7177     }
7178     else if (can_move_on && rnd > rnd_value / 8)
7179       MovDir[x][y] = old_move_dir;
7180     else if (can_turn_left && can_turn_right)
7181       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7182     else if (can_turn_left && rnd > rnd_value / 8)
7183       MovDir[x][y] = left_dir;
7184     else if (can_turn_right && rnd > rnd_value/8)
7185       MovDir[x][y] = right_dir;
7186     else
7187       MovDir[x][y] = back_dir;
7188
7189     xx = x + move_xy[MovDir[x][y]].dx;
7190     yy = y + move_xy[MovDir[x][y]].dy;
7191
7192     if (!IN_LEV_FIELD(xx, yy) ||
7193         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Tile[xx][yy])))
7194       MovDir[x][y] = old_move_dir;
7195
7196     MovDelay[x][y] = 0;
7197   }
7198   else if (element == EL_DRAGON)
7199   {
7200     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7201     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7202     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7203     int rnd_value = 24;
7204     int rnd = RND(rnd_value);
7205
7206     if (can_move_on && rnd > rnd_value / 8)
7207       MovDir[x][y] = old_move_dir;
7208     else if (can_turn_left && can_turn_right)
7209       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7210     else if (can_turn_left && rnd > rnd_value / 8)
7211       MovDir[x][y] = left_dir;
7212     else if (can_turn_right && rnd > rnd_value / 8)
7213       MovDir[x][y] = right_dir;
7214     else
7215       MovDir[x][y] = back_dir;
7216
7217     xx = x + move_xy[MovDir[x][y]].dx;
7218     yy = y + move_xy[MovDir[x][y]].dy;
7219
7220     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7221       MovDir[x][y] = old_move_dir;
7222
7223     MovDelay[x][y] = 0;
7224   }
7225   else if (element == EL_MOLE)
7226   {
7227     boolean can_move_on =
7228       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7229                             IS_AMOEBOID(Tile[move_x][move_y]) ||
7230                             Tile[move_x][move_y] == EL_AMOEBA_SHRINKING));
7231     if (!can_move_on)
7232     {
7233       boolean can_turn_left =
7234         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7235                               IS_AMOEBOID(Tile[left_x][left_y])));
7236
7237       boolean can_turn_right =
7238         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7239                               IS_AMOEBOID(Tile[right_x][right_y])));
7240
7241       if (can_turn_left && can_turn_right)
7242         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7243       else if (can_turn_left)
7244         MovDir[x][y] = left_dir;
7245       else
7246         MovDir[x][y] = right_dir;
7247     }
7248
7249     if (MovDir[x][y] != old_move_dir)
7250       MovDelay[x][y] = 9;
7251   }
7252   else if (element == EL_BALLOON)
7253   {
7254     MovDir[x][y] = game.wind_direction;
7255     MovDelay[x][y] = 0;
7256   }
7257   else if (element == EL_SPRING)
7258   {
7259     if (MovDir[x][y] & MV_HORIZONTAL)
7260     {
7261       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7262           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7263       {
7264         Tile[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7265         ResetGfxAnimation(move_x, move_y);
7266         TEST_DrawLevelField(move_x, move_y);
7267
7268         MovDir[x][y] = back_dir;
7269       }
7270       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7271                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7272         MovDir[x][y] = MV_NONE;
7273     }
7274
7275     MovDelay[x][y] = 0;
7276   }
7277   else if (element == EL_ROBOT ||
7278            element == EL_SATELLITE ||
7279            element == EL_PENGUIN ||
7280            element == EL_EMC_ANDROID)
7281   {
7282     int attr_x = -1, attr_y = -1;
7283
7284     if (game.all_players_gone)
7285     {
7286       attr_x = game.exit_x;
7287       attr_y = game.exit_y;
7288     }
7289     else
7290     {
7291       int i;
7292
7293       for (i = 0; i < MAX_PLAYERS; i++)
7294       {
7295         struct PlayerInfo *player = &stored_player[i];
7296         int jx = player->jx, jy = player->jy;
7297
7298         if (!player->active)
7299           continue;
7300
7301         if (attr_x == -1 ||
7302             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7303         {
7304           attr_x = jx;
7305           attr_y = jy;
7306         }
7307       }
7308     }
7309
7310     if (element == EL_ROBOT &&
7311         game.robot_wheel_x >= 0 &&
7312         game.robot_wheel_y >= 0 &&
7313         (Tile[game.robot_wheel_x][game.robot_wheel_y] == EL_ROBOT_WHEEL_ACTIVE ||
7314          game.engine_version < VERSION_IDENT(3,1,0,0)))
7315     {
7316       attr_x = game.robot_wheel_x;
7317       attr_y = game.robot_wheel_y;
7318     }
7319
7320     if (element == EL_PENGUIN)
7321     {
7322       int i;
7323       struct XY *xy = xy_topdown;
7324
7325       for (i = 0; i < NUM_DIRECTIONS; i++)
7326       {
7327         int ex = x + xy[i].x;
7328         int ey = y + xy[i].y;
7329
7330         if (IN_LEV_FIELD(ex, ey) && (Tile[ex][ey] == EL_EXIT_OPEN ||
7331                                      Tile[ex][ey] == EL_EM_EXIT_OPEN ||
7332                                      Tile[ex][ey] == EL_STEEL_EXIT_OPEN ||
7333                                      Tile[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7334         {
7335           attr_x = ex;
7336           attr_y = ey;
7337           break;
7338         }
7339       }
7340     }
7341
7342     MovDir[x][y] = MV_NONE;
7343     if (attr_x < x)
7344       MovDir[x][y] |= (game.all_players_gone ? MV_RIGHT : MV_LEFT);
7345     else if (attr_x > x)
7346       MovDir[x][y] |= (game.all_players_gone ? MV_LEFT : MV_RIGHT);
7347     if (attr_y < y)
7348       MovDir[x][y] |= (game.all_players_gone ? MV_DOWN : MV_UP);
7349     else if (attr_y > y)
7350       MovDir[x][y] |= (game.all_players_gone ? MV_UP : MV_DOWN);
7351
7352     if (element == EL_ROBOT)
7353     {
7354       int newx, newy;
7355
7356       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7357         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7358       Moving2Blocked(x, y, &newx, &newy);
7359
7360       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7361         MovDelay[x][y] = 8 + 8 * !RND(3);
7362       else
7363         MovDelay[x][y] = 16;
7364     }
7365     else if (element == EL_PENGUIN)
7366     {
7367       int newx, newy;
7368
7369       MovDelay[x][y] = 1;
7370
7371       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7372       {
7373         boolean first_horiz = RND(2);
7374         int new_move_dir = MovDir[x][y];
7375
7376         MovDir[x][y] =
7377           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7378         Moving2Blocked(x, y, &newx, &newy);
7379
7380         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7381           return;
7382
7383         MovDir[x][y] =
7384           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7385         Moving2Blocked(x, y, &newx, &newy);
7386
7387         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7388           return;
7389
7390         MovDir[x][y] = old_move_dir;
7391         return;
7392       }
7393     }
7394     else if (element == EL_SATELLITE)
7395     {
7396       int newx, newy;
7397
7398       MovDelay[x][y] = 1;
7399
7400       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7401       {
7402         boolean first_horiz = RND(2);
7403         int new_move_dir = MovDir[x][y];
7404
7405         MovDir[x][y] =
7406           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7407         Moving2Blocked(x, y, &newx, &newy);
7408
7409         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7410           return;
7411
7412         MovDir[x][y] =
7413           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7414         Moving2Blocked(x, y, &newx, &newy);
7415
7416         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7417           return;
7418
7419         MovDir[x][y] = old_move_dir;
7420         return;
7421       }
7422     }
7423     else if (element == EL_EMC_ANDROID)
7424     {
7425       static int check_pos[16] =
7426       {
7427         -1,             //  0 => (invalid)
7428         7,              //  1 => MV_LEFT
7429         3,              //  2 => MV_RIGHT
7430         -1,             //  3 => (invalid)
7431         1,              //  4 =>            MV_UP
7432         0,              //  5 => MV_LEFT  | MV_UP
7433         2,              //  6 => MV_RIGHT | MV_UP
7434         -1,             //  7 => (invalid)
7435         5,              //  8 =>            MV_DOWN
7436         6,              //  9 => MV_LEFT  | MV_DOWN
7437         4,              // 10 => MV_RIGHT | MV_DOWN
7438         -1,             // 11 => (invalid)
7439         -1,             // 12 => (invalid)
7440         -1,             // 13 => (invalid)
7441         -1,             // 14 => (invalid)
7442         -1,             // 15 => (invalid)
7443       };
7444       static struct
7445       {
7446         int dx, dy;
7447         int dir;
7448       } check_xy[8] =
7449       {
7450         { -1, -1,       MV_LEFT  | MV_UP   },
7451         {  0, -1,                  MV_UP   },
7452         { +1, -1,       MV_RIGHT | MV_UP   },
7453         { +1,  0,       MV_RIGHT           },
7454         { +1, +1,       MV_RIGHT | MV_DOWN },
7455         {  0, +1,                  MV_DOWN },
7456         { -1, +1,       MV_LEFT  | MV_DOWN },
7457         { -1,  0,       MV_LEFT            },
7458       };
7459       int start_pos, check_order;
7460       boolean can_clone = FALSE;
7461       int i;
7462
7463       // check if there is any free field around current position
7464       for (i = 0; i < 8; i++)
7465       {
7466         int newx = x + check_xy[i].dx;
7467         int newy = y + check_xy[i].dy;
7468
7469         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7470         {
7471           can_clone = TRUE;
7472
7473           break;
7474         }
7475       }
7476
7477       if (can_clone)            // randomly find an element to clone
7478       {
7479         can_clone = FALSE;
7480
7481         start_pos = check_pos[RND(8)];
7482         check_order = (RND(2) ? -1 : +1);
7483
7484         for (i = 0; i < 8; i++)
7485         {
7486           int pos_raw = start_pos + i * check_order;
7487           int pos = (pos_raw + 8) % 8;
7488           int newx = x + check_xy[pos].dx;
7489           int newy = y + check_xy[pos].dy;
7490
7491           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7492           {
7493             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7494             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7495
7496             Store[x][y] = Tile[newx][newy];
7497
7498             can_clone = TRUE;
7499
7500             break;
7501           }
7502         }
7503       }
7504
7505       if (can_clone)            // randomly find a direction to move
7506       {
7507         can_clone = FALSE;
7508
7509         start_pos = check_pos[RND(8)];
7510         check_order = (RND(2) ? -1 : +1);
7511
7512         for (i = 0; i < 8; i++)
7513         {
7514           int pos_raw = start_pos + i * check_order;
7515           int pos = (pos_raw + 8) % 8;
7516           int newx = x + check_xy[pos].dx;
7517           int newy = y + check_xy[pos].dy;
7518           int new_move_dir = check_xy[pos].dir;
7519
7520           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7521           {
7522             MovDir[x][y] = new_move_dir;
7523             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7524
7525             can_clone = TRUE;
7526
7527             break;
7528           }
7529         }
7530       }
7531
7532       if (can_clone)            // cloning and moving successful
7533         return;
7534
7535       // cannot clone -- try to move towards player
7536
7537       start_pos = check_pos[MovDir[x][y] & 0x0f];
7538       check_order = (RND(2) ? -1 : +1);
7539
7540       for (i = 0; i < 3; i++)
7541       {
7542         // first check start_pos, then previous/next or (next/previous) pos
7543         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7544         int pos = (pos_raw + 8) % 8;
7545         int newx = x + check_xy[pos].dx;
7546         int newy = y + check_xy[pos].dy;
7547         int new_move_dir = check_xy[pos].dir;
7548
7549         if (IS_PLAYER(newx, newy))
7550           break;
7551
7552         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7553         {
7554           MovDir[x][y] = new_move_dir;
7555           MovDelay[x][y] = level.android_move_time * 8 + 1;
7556
7557           break;
7558         }
7559       }
7560     }
7561   }
7562   else if (move_pattern == MV_TURNING_LEFT ||
7563            move_pattern == MV_TURNING_RIGHT ||
7564            move_pattern == MV_TURNING_LEFT_RIGHT ||
7565            move_pattern == MV_TURNING_RIGHT_LEFT ||
7566            move_pattern == MV_TURNING_RANDOM ||
7567            move_pattern == MV_ALL_DIRECTIONS)
7568   {
7569     boolean can_turn_left =
7570       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7571     boolean can_turn_right =
7572       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y);
7573
7574     if (element_info[element].move_stepsize == 0)       // "not moving"
7575       return;
7576
7577     if (move_pattern == MV_TURNING_LEFT)
7578       MovDir[x][y] = left_dir;
7579     else if (move_pattern == MV_TURNING_RIGHT)
7580       MovDir[x][y] = right_dir;
7581     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7582       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7583     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7584       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7585     else if (move_pattern == MV_TURNING_RANDOM)
7586       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7587                       can_turn_right && !can_turn_left ? right_dir :
7588                       RND(2) ? left_dir : right_dir);
7589     else if (can_turn_left && can_turn_right)
7590       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7591     else if (can_turn_left)
7592       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7593     else if (can_turn_right)
7594       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7595     else
7596       MovDir[x][y] = back_dir;
7597
7598     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7599   }
7600   else if (move_pattern == MV_HORIZONTAL ||
7601            move_pattern == MV_VERTICAL)
7602   {
7603     if (move_pattern & old_move_dir)
7604       MovDir[x][y] = back_dir;
7605     else if (move_pattern == MV_HORIZONTAL)
7606       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7607     else if (move_pattern == MV_VERTICAL)
7608       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7609
7610     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7611   }
7612   else if (move_pattern & MV_ANY_DIRECTION)
7613   {
7614     MovDir[x][y] = move_pattern;
7615     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7616   }
7617   else if (move_pattern & MV_WIND_DIRECTION)
7618   {
7619     MovDir[x][y] = game.wind_direction;
7620     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7621   }
7622   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7623   {
7624     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7625       MovDir[x][y] = left_dir;
7626     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7627       MovDir[x][y] = right_dir;
7628
7629     if (MovDir[x][y] != old_move_dir)
7630       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7631   }
7632   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7633   {
7634     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7635       MovDir[x][y] = right_dir;
7636     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7637       MovDir[x][y] = left_dir;
7638
7639     if (MovDir[x][y] != old_move_dir)
7640       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7641   }
7642   else if (move_pattern == MV_TOWARDS_PLAYER ||
7643            move_pattern == MV_AWAY_FROM_PLAYER)
7644   {
7645     int attr_x = -1, attr_y = -1;
7646     int newx, newy;
7647     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7648
7649     if (game.all_players_gone)
7650     {
7651       attr_x = game.exit_x;
7652       attr_y = game.exit_y;
7653     }
7654     else
7655     {
7656       int i;
7657
7658       for (i = 0; i < MAX_PLAYERS; i++)
7659       {
7660         struct PlayerInfo *player = &stored_player[i];
7661         int jx = player->jx, jy = player->jy;
7662
7663         if (!player->active)
7664           continue;
7665
7666         if (attr_x == -1 ||
7667             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7668         {
7669           attr_x = jx;
7670           attr_y = jy;
7671         }
7672       }
7673     }
7674
7675     MovDir[x][y] = MV_NONE;
7676     if (attr_x < x)
7677       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7678     else if (attr_x > x)
7679       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7680     if (attr_y < y)
7681       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7682     else if (attr_y > y)
7683       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7684
7685     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7686
7687     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7688     {
7689       boolean first_horiz = RND(2);
7690       int new_move_dir = MovDir[x][y];
7691
7692       if (element_info[element].move_stepsize == 0)     // "not moving"
7693       {
7694         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7695         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7696
7697         return;
7698       }
7699
7700       MovDir[x][y] =
7701         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7702       Moving2Blocked(x, y, &newx, &newy);
7703
7704       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7705         return;
7706
7707       MovDir[x][y] =
7708         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7709       Moving2Blocked(x, y, &newx, &newy);
7710
7711       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7712         return;
7713
7714       MovDir[x][y] = old_move_dir;
7715     }
7716   }
7717   else if (move_pattern == MV_WHEN_PUSHED ||
7718            move_pattern == MV_WHEN_DROPPED)
7719   {
7720     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7721       MovDir[x][y] = MV_NONE;
7722
7723     MovDelay[x][y] = 0;
7724   }
7725   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7726   {
7727     struct XY *test_xy = xy_topdown;
7728     static int test_dir[4] =
7729     {
7730       MV_UP,
7731       MV_LEFT,
7732       MV_RIGHT,
7733       MV_DOWN
7734     };
7735     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7736     int move_preference = -1000000;     // start with very low preference
7737     int new_move_dir = MV_NONE;
7738     int start_test = RND(4);
7739     int i;
7740
7741     for (i = 0; i < NUM_DIRECTIONS; i++)
7742     {
7743       int j = (start_test + i) % 4;
7744       int move_dir = test_dir[j];
7745       int move_dir_preference;
7746
7747       xx = x + test_xy[j].x;
7748       yy = y + test_xy[j].y;
7749
7750       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7751           (IS_PLAYER(xx, yy) || Tile[xx][yy] == EL_PLAYER_IS_LEAVING))
7752       {
7753         new_move_dir = move_dir;
7754
7755         break;
7756       }
7757
7758       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7759         continue;
7760
7761       move_dir_preference = -1 * RunnerVisit[xx][yy];
7762       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7763         move_dir_preference = PlayerVisit[xx][yy];
7764
7765       if (move_dir_preference > move_preference)
7766       {
7767         // prefer field that has not been visited for the longest time
7768         move_preference = move_dir_preference;
7769         new_move_dir = move_dir;
7770       }
7771       else if (move_dir_preference == move_preference &&
7772                move_dir == old_move_dir)
7773       {
7774         // prefer last direction when all directions are preferred equally
7775         move_preference = move_dir_preference;
7776         new_move_dir = move_dir;
7777       }
7778     }
7779
7780     MovDir[x][y] = new_move_dir;
7781     if (old_move_dir != new_move_dir)
7782       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7783   }
7784 }
7785
7786 static void TurnRound(int x, int y)
7787 {
7788   int direction = MovDir[x][y];
7789
7790   TurnRoundExt(x, y);
7791
7792   GfxDir[x][y] = MovDir[x][y];
7793
7794   if (direction != MovDir[x][y])
7795     GfxFrame[x][y] = 0;
7796
7797   if (MovDelay[x][y])
7798     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7799
7800   ResetGfxFrame(x, y);
7801 }
7802
7803 static boolean JustBeingPushed(int x, int y)
7804 {
7805   int i;
7806
7807   for (i = 0; i < MAX_PLAYERS; i++)
7808   {
7809     struct PlayerInfo *player = &stored_player[i];
7810
7811     if (player->active && player->is_pushing && player->MovPos)
7812     {
7813       int next_jx = player->jx + (player->jx - player->last_jx);
7814       int next_jy = player->jy + (player->jy - player->last_jy);
7815
7816       if (x == next_jx && y == next_jy)
7817         return TRUE;
7818     }
7819   }
7820
7821   return FALSE;
7822 }
7823
7824 static void StartMoving(int x, int y)
7825 {
7826   boolean started_moving = FALSE;       // some elements can fall _and_ move
7827   int element = Tile[x][y];
7828
7829   if (Stop[x][y])
7830     return;
7831
7832   if (MovDelay[x][y] == 0)
7833     GfxAction[x][y] = ACTION_DEFAULT;
7834
7835   if (CAN_FALL(element) && y < lev_fieldy - 1)
7836   {
7837     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7838         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7839       if (JustBeingPushed(x, y))
7840         return;
7841
7842     if (element == EL_QUICKSAND_FULL)
7843     {
7844       if (IS_FREE(x, y + 1))
7845       {
7846         InitMovingField(x, y, MV_DOWN);
7847         started_moving = TRUE;
7848
7849         Tile[x][y] = EL_QUICKSAND_EMPTYING;
7850 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7851         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7852           Store[x][y] = EL_ROCK;
7853 #else
7854         Store[x][y] = EL_ROCK;
7855 #endif
7856
7857         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7858       }
7859       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7860       {
7861         if (!MovDelay[x][y])
7862         {
7863           MovDelay[x][y] = TILEY + 1;
7864
7865           ResetGfxAnimation(x, y);
7866           ResetGfxAnimation(x, y + 1);
7867         }
7868
7869         if (MovDelay[x][y])
7870         {
7871           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7872           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7873
7874           MovDelay[x][y]--;
7875           if (MovDelay[x][y])
7876             return;
7877         }
7878
7879         Tile[x][y] = EL_QUICKSAND_EMPTY;
7880         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7881         Store[x][y + 1] = Store[x][y];
7882         Store[x][y] = 0;
7883
7884         PlayLevelSoundAction(x, y, ACTION_FILLING);
7885       }
7886       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7887       {
7888         if (!MovDelay[x][y])
7889         {
7890           MovDelay[x][y] = TILEY + 1;
7891
7892           ResetGfxAnimation(x, y);
7893           ResetGfxAnimation(x, y + 1);
7894         }
7895
7896         if (MovDelay[x][y])
7897         {
7898           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7899           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7900
7901           MovDelay[x][y]--;
7902           if (MovDelay[x][y])
7903             return;
7904         }
7905
7906         Tile[x][y] = EL_QUICKSAND_EMPTY;
7907         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7908         Store[x][y + 1] = Store[x][y];
7909         Store[x][y] = 0;
7910
7911         PlayLevelSoundAction(x, y, ACTION_FILLING);
7912       }
7913     }
7914     else if (element == EL_QUICKSAND_FAST_FULL)
7915     {
7916       if (IS_FREE(x, y + 1))
7917       {
7918         InitMovingField(x, y, MV_DOWN);
7919         started_moving = TRUE;
7920
7921         Tile[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7922 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7923         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7924           Store[x][y] = EL_ROCK;
7925 #else
7926         Store[x][y] = EL_ROCK;
7927 #endif
7928
7929         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7930       }
7931       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7932       {
7933         if (!MovDelay[x][y])
7934         {
7935           MovDelay[x][y] = TILEY + 1;
7936
7937           ResetGfxAnimation(x, y);
7938           ResetGfxAnimation(x, y + 1);
7939         }
7940
7941         if (MovDelay[x][y])
7942         {
7943           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7944           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7945
7946           MovDelay[x][y]--;
7947           if (MovDelay[x][y])
7948             return;
7949         }
7950
7951         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7952         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7953         Store[x][y + 1] = Store[x][y];
7954         Store[x][y] = 0;
7955
7956         PlayLevelSoundAction(x, y, ACTION_FILLING);
7957       }
7958       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7959       {
7960         if (!MovDelay[x][y])
7961         {
7962           MovDelay[x][y] = TILEY + 1;
7963
7964           ResetGfxAnimation(x, y);
7965           ResetGfxAnimation(x, y + 1);
7966         }
7967
7968         if (MovDelay[x][y])
7969         {
7970           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7971           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7972
7973           MovDelay[x][y]--;
7974           if (MovDelay[x][y])
7975             return;
7976         }
7977
7978         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7979         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7980         Store[x][y + 1] = Store[x][y];
7981         Store[x][y] = 0;
7982
7983         PlayLevelSoundAction(x, y, ACTION_FILLING);
7984       }
7985     }
7986     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7987              Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7988     {
7989       InitMovingField(x, y, MV_DOWN);
7990       started_moving = TRUE;
7991
7992       Tile[x][y] = EL_QUICKSAND_FILLING;
7993       Store[x][y] = element;
7994
7995       PlayLevelSoundAction(x, y, ACTION_FILLING);
7996     }
7997     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7998              Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7999     {
8000       InitMovingField(x, y, MV_DOWN);
8001       started_moving = TRUE;
8002
8003       Tile[x][y] = EL_QUICKSAND_FAST_FILLING;
8004       Store[x][y] = element;
8005
8006       PlayLevelSoundAction(x, y, ACTION_FILLING);
8007     }
8008     else if (element == EL_MAGIC_WALL_FULL)
8009     {
8010       if (IS_FREE(x, y + 1))
8011       {
8012         InitMovingField(x, y, MV_DOWN);
8013         started_moving = TRUE;
8014
8015         Tile[x][y] = EL_MAGIC_WALL_EMPTYING;
8016         Store[x][y] = EL_CHANGED(Store[x][y]);
8017       }
8018       else if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
8019       {
8020         if (!MovDelay[x][y])
8021           MovDelay[x][y] = TILEY / 4 + 1;
8022
8023         if (MovDelay[x][y])
8024         {
8025           MovDelay[x][y]--;
8026           if (MovDelay[x][y])
8027             return;
8028         }
8029
8030         Tile[x][y] = EL_MAGIC_WALL_ACTIVE;
8031         Tile[x][y + 1] = EL_MAGIC_WALL_FULL;
8032         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
8033         Store[x][y] = 0;
8034       }
8035     }
8036     else if (element == EL_BD_MAGIC_WALL_FULL)
8037     {
8038       if (IS_FREE(x, y + 1))
8039       {
8040         InitMovingField(x, y, MV_DOWN);
8041         started_moving = TRUE;
8042
8043         Tile[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
8044         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
8045       }
8046       else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
8047       {
8048         if (!MovDelay[x][y])
8049           MovDelay[x][y] = TILEY / 4 + 1;
8050
8051         if (MovDelay[x][y])
8052         {
8053           MovDelay[x][y]--;
8054           if (MovDelay[x][y])
8055             return;
8056         }
8057
8058         Tile[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
8059         Tile[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
8060         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
8061         Store[x][y] = 0;
8062       }
8063     }
8064     else if (element == EL_DC_MAGIC_WALL_FULL)
8065     {
8066       if (IS_FREE(x, y + 1))
8067       {
8068         InitMovingField(x, y, MV_DOWN);
8069         started_moving = TRUE;
8070
8071         Tile[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
8072         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
8073       }
8074       else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
8075       {
8076         if (!MovDelay[x][y])
8077           MovDelay[x][y] = TILEY / 4 + 1;
8078
8079         if (MovDelay[x][y])
8080         {
8081           MovDelay[x][y]--;
8082           if (MovDelay[x][y])
8083             return;
8084         }
8085
8086         Tile[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
8087         Tile[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
8088         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
8089         Store[x][y] = 0;
8090       }
8091     }
8092     else if ((CAN_PASS_MAGIC_WALL(element) &&
8093               (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
8094                Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
8095              (CAN_PASS_DC_MAGIC_WALL(element) &&
8096               (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
8097
8098     {
8099       InitMovingField(x, y, MV_DOWN);
8100       started_moving = TRUE;
8101
8102       Tile[x][y] =
8103         (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
8104          Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
8105          EL_DC_MAGIC_WALL_FILLING);
8106       Store[x][y] = element;
8107     }
8108     else if (CAN_FALL(element) && Tile[x][y + 1] == EL_ACID)
8109     {
8110       SplashAcid(x, y + 1);
8111
8112       InitMovingField(x, y, MV_DOWN);
8113       started_moving = TRUE;
8114
8115       Store[x][y] = EL_ACID;
8116     }
8117     else if (
8118              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8119               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
8120              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
8121               CAN_FALL(element) && WasJustFalling[x][y] &&
8122               (Tile[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
8123
8124              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
8125               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
8126               (Tile[x][y + 1] == EL_BLOCKED)))
8127     {
8128       /* this is needed for a special case not covered by calling "Impact()"
8129          from "ContinueMoving()": if an element moves to a tile directly below
8130          another element which was just falling on that tile (which was empty
8131          in the previous frame), the falling element above would just stop
8132          instead of smashing the element below (in previous version, the above
8133          element was just checked for "moving" instead of "falling", resulting
8134          in incorrect smashes caused by horizontal movement of the above
8135          element; also, the case of the player being the element to smash was
8136          simply not covered here... :-/ ) */
8137
8138       CheckCollision[x][y] = 0;
8139       CheckImpact[x][y] = 0;
8140
8141       Impact(x, y);
8142     }
8143     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
8144     {
8145       if (MovDir[x][y] == MV_NONE)
8146       {
8147         InitMovingField(x, y, MV_DOWN);
8148         started_moving = TRUE;
8149       }
8150     }
8151     else if (IS_FREE(x, y + 1) || Tile[x][y + 1] == EL_DIAMOND_BREAKING)
8152     {
8153       if (WasJustFalling[x][y]) // prevent animation from being restarted
8154         MovDir[x][y] = MV_DOWN;
8155
8156       InitMovingField(x, y, MV_DOWN);
8157       started_moving = TRUE;
8158     }
8159     else if (element == EL_AMOEBA_DROP)
8160     {
8161       Tile[x][y] = EL_AMOEBA_GROWING;
8162       Store[x][y] = EL_AMOEBA_WET;
8163     }
8164     else if (((IS_SLIPPERY(Tile[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8165               (IS_EM_SLIPPERY_WALL(Tile[x][y + 1]) && IS_GEM(element))) &&
8166              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8167              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8168     {
8169       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
8170                                 (IS_FREE(x - 1, y + 1) ||
8171                                  Tile[x - 1][y + 1] == EL_ACID));
8172       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8173                                 (IS_FREE(x + 1, y + 1) ||
8174                                  Tile[x + 1][y + 1] == EL_ACID));
8175       boolean can_fall_any  = (can_fall_left || can_fall_right);
8176       boolean can_fall_both = (can_fall_left && can_fall_right);
8177       int slippery_type = element_info[Tile[x][y + 1]].slippery_type;
8178
8179       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8180       {
8181         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8182           can_fall_right = FALSE;
8183         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8184           can_fall_left = FALSE;
8185         else if (slippery_type == SLIPPERY_ONLY_LEFT)
8186           can_fall_right = FALSE;
8187         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8188           can_fall_left = FALSE;
8189
8190         can_fall_any  = (can_fall_left || can_fall_right);
8191         can_fall_both = FALSE;
8192       }
8193
8194       if (can_fall_both)
8195       {
8196         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8197           can_fall_right = FALSE;       // slip down on left side
8198         else
8199           can_fall_left = !(can_fall_right = RND(2));
8200
8201         can_fall_both = FALSE;
8202       }
8203
8204       if (can_fall_any)
8205       {
8206         // if not determined otherwise, prefer left side for slipping down
8207         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8208         started_moving = TRUE;
8209       }
8210     }
8211     else if (IS_BELT_ACTIVE(Tile[x][y + 1]))
8212     {
8213       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
8214       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8215       int belt_nr = getBeltNrFromBeltActiveElement(Tile[x][y + 1]);
8216       int belt_dir = game.belt_dir[belt_nr];
8217
8218       if ((belt_dir == MV_LEFT  && left_is_free) ||
8219           (belt_dir == MV_RIGHT && right_is_free))
8220       {
8221         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8222
8223         InitMovingField(x, y, belt_dir);
8224         started_moving = TRUE;
8225
8226         Pushed[x][y] = TRUE;
8227         Pushed[nextx][y] = TRUE;
8228
8229         GfxAction[x][y] = ACTION_DEFAULT;
8230       }
8231       else
8232       {
8233         MovDir[x][y] = 0;       // if element was moving, stop it
8234       }
8235     }
8236   }
8237
8238   // not "else if" because of elements that can fall and move (EL_SPRING)
8239   if (CAN_MOVE(element) && !started_moving)
8240   {
8241     int move_pattern = element_info[element].move_pattern;
8242     int newx, newy;
8243
8244     Moving2Blocked(x, y, &newx, &newy);
8245
8246     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8247       return;
8248
8249     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8250         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8251     {
8252       WasJustMoving[x][y] = 0;
8253       CheckCollision[x][y] = 0;
8254
8255       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8256
8257       if (Tile[x][y] != element)        // element has changed
8258         return;
8259     }
8260
8261     if (!MovDelay[x][y])        // start new movement phase
8262     {
8263       // all objects that can change their move direction after each step
8264       // (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall
8265
8266       if (element != EL_YAMYAM &&
8267           element != EL_DARK_YAMYAM &&
8268           element != EL_PACMAN &&
8269           !(move_pattern & MV_ANY_DIRECTION) &&
8270           move_pattern != MV_TURNING_LEFT &&
8271           move_pattern != MV_TURNING_RIGHT &&
8272           move_pattern != MV_TURNING_LEFT_RIGHT &&
8273           move_pattern != MV_TURNING_RIGHT_LEFT &&
8274           move_pattern != MV_TURNING_RANDOM)
8275       {
8276         TurnRound(x, y);
8277
8278         if (MovDelay[x][y] && (element == EL_BUG ||
8279                                element == EL_SPACESHIP ||
8280                                element == EL_SP_SNIKSNAK ||
8281                                element == EL_SP_ELECTRON ||
8282                                element == EL_MOLE))
8283           TEST_DrawLevelField(x, y);
8284       }
8285     }
8286
8287     if (MovDelay[x][y])         // wait some time before next movement
8288     {
8289       MovDelay[x][y]--;
8290
8291       if (element == EL_ROBOT ||
8292           element == EL_YAMYAM ||
8293           element == EL_DARK_YAMYAM)
8294       {
8295         DrawLevelElementAnimationIfNeeded(x, y, element);
8296         PlayLevelSoundAction(x, y, ACTION_WAITING);
8297       }
8298       else if (element == EL_SP_ELECTRON)
8299         DrawLevelElementAnimationIfNeeded(x, y, element);
8300       else if (element == EL_DRAGON)
8301       {
8302         int i;
8303         int dir = MovDir[x][y];
8304         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8305         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8306         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8307                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8308                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8309                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8310         int frame = getGraphicAnimationFrameXY(graphic, x, y);
8311
8312         GfxAction[x][y] = ACTION_ATTACKING;
8313
8314         if (IS_PLAYER(x, y))
8315           DrawPlayerField(x, y);
8316         else
8317           TEST_DrawLevelField(x, y);
8318
8319         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8320
8321         for (i = 1; i <= 3; i++)
8322         {
8323           int xx = x + i * dx;
8324           int yy = y + i * dy;
8325           int sx = SCREENX(xx);
8326           int sy = SCREENY(yy);
8327           int flame_graphic = graphic + (i - 1);
8328
8329           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Tile[xx][yy]))
8330             break;
8331
8332           if (MovDelay[x][y])
8333           {
8334             int flamed = MovingOrBlocked2Element(xx, yy);
8335
8336             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8337               Bang(xx, yy);
8338             else
8339               RemoveMovingField(xx, yy);
8340
8341             ChangeDelay[xx][yy] = 0;
8342
8343             Tile[xx][yy] = EL_FLAMES;
8344
8345             if (IN_SCR_FIELD(sx, sy))
8346             {
8347               TEST_DrawLevelFieldCrumbled(xx, yy);
8348               DrawScreenGraphic(sx, sy, flame_graphic, frame);
8349             }
8350           }
8351           else
8352           {
8353             if (Tile[xx][yy] == EL_FLAMES)
8354               Tile[xx][yy] = EL_EMPTY;
8355             TEST_DrawLevelField(xx, yy);
8356           }
8357         }
8358       }
8359
8360       if (MovDelay[x][y])       // element still has to wait some time
8361       {
8362         PlayLevelSoundAction(x, y, ACTION_WAITING);
8363
8364         return;
8365       }
8366     }
8367
8368     // now make next step
8369
8370     Moving2Blocked(x, y, &newx, &newy); // get next screen position
8371
8372     if (DONT_COLLIDE_WITH(element) &&
8373         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8374         !PLAYER_ENEMY_PROTECTED(newx, newy))
8375     {
8376       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8377
8378       return;
8379     }
8380
8381     else if (CAN_MOVE_INTO_ACID(element) &&
8382              IN_LEV_FIELD(newx, newy) && Tile[newx][newy] == EL_ACID &&
8383              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8384              (MovDir[x][y] == MV_DOWN ||
8385               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8386     {
8387       SplashAcid(newx, newy);
8388       Store[x][y] = EL_ACID;
8389     }
8390     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8391     {
8392       if (Tile[newx][newy] == EL_EXIT_OPEN ||
8393           Tile[newx][newy] == EL_EM_EXIT_OPEN ||
8394           Tile[newx][newy] == EL_STEEL_EXIT_OPEN ||
8395           Tile[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8396       {
8397         RemoveField(x, y);
8398         TEST_DrawLevelField(x, y);
8399
8400         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8401         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8402           DrawGraphicThruMask(SCREENX(newx), SCREENY(newy), el2img(element), 0);
8403
8404         game.friends_still_needed--;
8405         if (!game.friends_still_needed &&
8406             !game.GameOver &&
8407             game.all_players_gone)
8408           LevelSolved();
8409
8410         return;
8411       }
8412       else if (IS_FOOD_PENGUIN(Tile[newx][newy]))
8413       {
8414         if (DigField(local_player, x, y, newx, newy, 0, 0, DF_DIG) == MP_MOVING)
8415           TEST_DrawLevelField(newx, newy);
8416         else
8417           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8418       }
8419       else if (!IS_FREE(newx, newy))
8420       {
8421         GfxAction[x][y] = ACTION_WAITING;
8422
8423         if (IS_PLAYER(x, y))
8424           DrawPlayerField(x, y);
8425         else
8426           TEST_DrawLevelField(x, y);
8427
8428         return;
8429       }
8430     }
8431     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8432     {
8433       if (IS_FOOD_PIG(Tile[newx][newy]))
8434       {
8435         if (IS_MOVING(newx, newy))
8436           RemoveMovingField(newx, newy);
8437         else
8438         {
8439           Tile[newx][newy] = EL_EMPTY;
8440           TEST_DrawLevelField(newx, newy);
8441         }
8442
8443         PlayLevelSound(x, y, SND_PIG_DIGGING);
8444       }
8445       else if (!IS_FREE(newx, newy))
8446       {
8447         if (IS_PLAYER(x, y))
8448           DrawPlayerField(x, y);
8449         else
8450           TEST_DrawLevelField(x, y);
8451
8452         return;
8453       }
8454     }
8455     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8456     {
8457       if (Store[x][y] != EL_EMPTY)
8458       {
8459         boolean can_clone = FALSE;
8460         int xx, yy;
8461
8462         // check if element to clone is still there
8463         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8464         {
8465           if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == Store[x][y])
8466           {
8467             can_clone = TRUE;
8468
8469             break;
8470           }
8471         }
8472
8473         // cannot clone or target field not free anymore -- do not clone
8474         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8475           Store[x][y] = EL_EMPTY;
8476       }
8477
8478       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8479       {
8480         if (IS_MV_DIAGONAL(MovDir[x][y]))
8481         {
8482           int diagonal_move_dir = MovDir[x][y];
8483           int stored = Store[x][y];
8484           int change_delay = 8;
8485           int graphic;
8486
8487           // android is moving diagonally
8488
8489           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8490
8491           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8492           GfxElement[x][y] = EL_EMC_ANDROID;
8493           GfxAction[x][y] = ACTION_SHRINKING;
8494           GfxDir[x][y] = diagonal_move_dir;
8495           ChangeDelay[x][y] = change_delay;
8496
8497           if (Store[x][y] == EL_EMPTY)
8498             Store[x][y] = GfxElementEmpty[x][y];
8499
8500           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8501                                    GfxDir[x][y]);
8502
8503           DrawLevelGraphicAnimation(x, y, graphic);
8504           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8505
8506           if (Tile[newx][newy] == EL_ACID)
8507           {
8508             SplashAcid(newx, newy);
8509
8510             return;
8511           }
8512
8513           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8514
8515           Store[newx][newy] = EL_EMC_ANDROID;
8516           GfxElement[newx][newy] = EL_EMC_ANDROID;
8517           GfxAction[newx][newy] = ACTION_GROWING;
8518           GfxDir[newx][newy] = diagonal_move_dir;
8519           ChangeDelay[newx][newy] = change_delay;
8520
8521           graphic = el_act_dir2img(GfxElement[newx][newy],
8522                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8523
8524           DrawLevelGraphicAnimation(newx, newy, graphic);
8525           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8526
8527           return;
8528         }
8529         else
8530         {
8531           Tile[newx][newy] = EL_EMPTY;
8532           TEST_DrawLevelField(newx, newy);
8533
8534           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8535         }
8536       }
8537       else if (!IS_FREE(newx, newy))
8538       {
8539         return;
8540       }
8541     }
8542     else if (IS_CUSTOM_ELEMENT(element) &&
8543              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8544     {
8545       if (!DigFieldByCE(newx, newy, element))
8546         return;
8547
8548       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8549       {
8550         RunnerVisit[x][y] = FrameCounter;
8551         PlayerVisit[x][y] /= 8;         // expire player visit path
8552       }
8553     }
8554     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8555     {
8556       if (!IS_FREE(newx, newy))
8557       {
8558         if (IS_PLAYER(x, y))
8559           DrawPlayerField(x, y);
8560         else
8561           TEST_DrawLevelField(x, y);
8562
8563         return;
8564       }
8565       else
8566       {
8567         boolean wanna_flame = !RND(10);
8568         int dx = newx - x, dy = newy - y;
8569         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8570         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8571         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8572                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8573         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8574                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8575
8576         if ((wanna_flame ||
8577              IS_CLASSIC_ENEMY(element1) ||
8578              IS_CLASSIC_ENEMY(element2)) &&
8579             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8580             element1 != EL_FLAMES && element2 != EL_FLAMES)
8581         {
8582           ResetGfxAnimation(x, y);
8583           GfxAction[x][y] = ACTION_ATTACKING;
8584
8585           if (IS_PLAYER(x, y))
8586             DrawPlayerField(x, y);
8587           else
8588             TEST_DrawLevelField(x, y);
8589
8590           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8591
8592           MovDelay[x][y] = 50;
8593
8594           Tile[newx][newy] = EL_FLAMES;
8595           if (IN_LEV_FIELD(newx1, newy1) && Tile[newx1][newy1] == EL_EMPTY)
8596             Tile[newx1][newy1] = EL_FLAMES;
8597           if (IN_LEV_FIELD(newx2, newy2) && Tile[newx2][newy2] == EL_EMPTY)
8598             Tile[newx2][newy2] = EL_FLAMES;
8599
8600           return;
8601         }
8602       }
8603     }
8604     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8605              Tile[newx][newy] == EL_DIAMOND)
8606     {
8607       if (IS_MOVING(newx, newy))
8608         RemoveMovingField(newx, newy);
8609       else
8610       {
8611         Tile[newx][newy] = EL_EMPTY;
8612         TEST_DrawLevelField(newx, newy);
8613       }
8614
8615       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8616     }
8617     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8618              IS_FOOD_DARK_YAMYAM(Tile[newx][newy]))
8619     {
8620       if (AmoebaNr[newx][newy])
8621       {
8622         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8623         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8624             Tile[newx][newy] == EL_BD_AMOEBA)
8625           AmoebaCnt[AmoebaNr[newx][newy]]--;
8626       }
8627
8628       if (IS_MOVING(newx, newy))
8629       {
8630         RemoveMovingField(newx, newy);
8631       }
8632       else
8633       {
8634         Tile[newx][newy] = EL_EMPTY;
8635         TEST_DrawLevelField(newx, newy);
8636       }
8637
8638       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8639     }
8640     else if ((element == EL_PACMAN || element == EL_MOLE)
8641              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Tile[newx][newy]))
8642     {
8643       if (AmoebaNr[newx][newy])
8644       {
8645         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8646         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8647             Tile[newx][newy] == EL_BD_AMOEBA)
8648           AmoebaCnt[AmoebaNr[newx][newy]]--;
8649       }
8650
8651       if (element == EL_MOLE)
8652       {
8653         Tile[newx][newy] = EL_AMOEBA_SHRINKING;
8654         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8655
8656         ResetGfxAnimation(x, y);
8657         GfxAction[x][y] = ACTION_DIGGING;
8658         TEST_DrawLevelField(x, y);
8659
8660         MovDelay[newx][newy] = 0;       // start amoeba shrinking delay
8661
8662         return;                         // wait for shrinking amoeba
8663       }
8664       else      // element == EL_PACMAN
8665       {
8666         Tile[newx][newy] = EL_EMPTY;
8667         TEST_DrawLevelField(newx, newy);
8668         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8669       }
8670     }
8671     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8672              (Tile[newx][newy] == EL_AMOEBA_SHRINKING ||
8673               (Tile[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8674     {
8675       // wait for shrinking amoeba to completely disappear
8676       return;
8677     }
8678     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8679     {
8680       // object was running against a wall
8681
8682       TurnRound(x, y);
8683
8684       if (GFX_ELEMENT(element) != EL_SAND)     // !!! FIX THIS (crumble) !!!
8685         DrawLevelElementAnimation(x, y, element);
8686
8687       if (DONT_TOUCH(element))
8688         TestIfBadThingTouchesPlayer(x, y);
8689
8690       return;
8691     }
8692
8693     InitMovingField(x, y, MovDir[x][y]);
8694
8695     PlayLevelSoundAction(x, y, ACTION_MOVING);
8696   }
8697
8698   if (MovDir[x][y])
8699     ContinueMoving(x, y);
8700 }
8701
8702 void ContinueMoving(int x, int y)
8703 {
8704   int element = Tile[x][y];
8705   struct ElementInfo *ei = &element_info[element];
8706   int direction = MovDir[x][y];
8707   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8708   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8709   int newx = x + dx, newy = y + dy;
8710   int stored = Store[x][y];
8711   int stored_new = Store[newx][newy];
8712   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8713   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8714   boolean last_line = (newy == lev_fieldy - 1);
8715   boolean use_step_delay = (GET_MAX_STEP_DELAY(element) != 0);
8716
8717   if (pushed_by_player)         // special case: moving object pushed by player
8718   {
8719     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x, y)->MovPos));
8720   }
8721   else if (use_step_delay)      // special case: moving object has step delay
8722   {
8723     if (!MovDelay[x][y])
8724       MovPos[x][y] += getElementMoveStepsize(x, y);
8725
8726     if (MovDelay[x][y])
8727       MovDelay[x][y]--;
8728     else
8729       MovDelay[x][y] = GET_NEW_STEP_DELAY(element);
8730
8731     if (MovDelay[x][y])
8732     {
8733       TEST_DrawLevelField(x, y);
8734
8735       return;   // element is still waiting
8736     }
8737   }
8738   else                          // normal case: generically moving object
8739   {
8740     MovPos[x][y] += getElementMoveStepsize(x, y);
8741   }
8742
8743   if (ABS(MovPos[x][y]) < TILEX)
8744   {
8745     TEST_DrawLevelField(x, y);
8746
8747     return;     // element is still moving
8748   }
8749
8750   // element reached destination field
8751
8752   Tile[x][y] = EL_EMPTY;
8753   Tile[newx][newy] = element;
8754   MovPos[x][y] = 0;     // force "not moving" for "crumbled sand"
8755
8756   if (Store[x][y] == EL_ACID)   // element is moving into acid pool
8757   {
8758     element = Tile[newx][newy] = EL_ACID;
8759   }
8760   else if (element == EL_MOLE)
8761   {
8762     Tile[x][y] = EL_SAND;
8763
8764     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8765   }
8766   else if (element == EL_QUICKSAND_FILLING)
8767   {
8768     element = Tile[newx][newy] = get_next_element(element);
8769     Store[newx][newy] = Store[x][y];
8770   }
8771   else if (element == EL_QUICKSAND_EMPTYING)
8772   {
8773     Tile[x][y] = get_next_element(element);
8774     element = Tile[newx][newy] = Store[x][y];
8775   }
8776   else if (element == EL_QUICKSAND_FAST_FILLING)
8777   {
8778     element = Tile[newx][newy] = get_next_element(element);
8779     Store[newx][newy] = Store[x][y];
8780   }
8781   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8782   {
8783     Tile[x][y] = get_next_element(element);
8784     element = Tile[newx][newy] = Store[x][y];
8785   }
8786   else if (element == EL_MAGIC_WALL_FILLING)
8787   {
8788     element = Tile[newx][newy] = get_next_element(element);
8789     if (!game.magic_wall_active)
8790       element = Tile[newx][newy] = EL_MAGIC_WALL_DEAD;
8791     Store[newx][newy] = Store[x][y];
8792   }
8793   else if (element == EL_MAGIC_WALL_EMPTYING)
8794   {
8795     Tile[x][y] = get_next_element(element);
8796     if (!game.magic_wall_active)
8797       Tile[x][y] = EL_MAGIC_WALL_DEAD;
8798     element = Tile[newx][newy] = Store[x][y];
8799
8800     InitField(newx, newy, FALSE);
8801   }
8802   else if (element == EL_BD_MAGIC_WALL_FILLING)
8803   {
8804     element = Tile[newx][newy] = get_next_element(element);
8805     if (!game.magic_wall_active)
8806       element = Tile[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8807     Store[newx][newy] = Store[x][y];
8808   }
8809   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8810   {
8811     Tile[x][y] = get_next_element(element);
8812     if (!game.magic_wall_active)
8813       Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
8814     element = Tile[newx][newy] = Store[x][y];
8815
8816     InitField(newx, newy, FALSE);
8817   }
8818   else if (element == EL_DC_MAGIC_WALL_FILLING)
8819   {
8820     element = Tile[newx][newy] = get_next_element(element);
8821     if (!game.magic_wall_active)
8822       element = Tile[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8823     Store[newx][newy] = Store[x][y];
8824   }
8825   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8826   {
8827     Tile[x][y] = get_next_element(element);
8828     if (!game.magic_wall_active)
8829       Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
8830     element = Tile[newx][newy] = Store[x][y];
8831
8832     InitField(newx, newy, FALSE);
8833   }
8834   else if (element == EL_AMOEBA_DROPPING)
8835   {
8836     Tile[x][y] = get_next_element(element);
8837     element = Tile[newx][newy] = Store[x][y];
8838   }
8839   else if (element == EL_SOKOBAN_OBJECT)
8840   {
8841     if (Back[x][y])
8842       Tile[x][y] = Back[x][y];
8843
8844     if (Back[newx][newy])
8845       Tile[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8846
8847     Back[x][y] = Back[newx][newy] = 0;
8848   }
8849
8850   Store[x][y] = EL_EMPTY;
8851   MovPos[x][y] = 0;
8852   MovDir[x][y] = 0;
8853   MovDelay[x][y] = 0;
8854
8855   MovDelay[newx][newy] = 0;
8856
8857   if (CAN_CHANGE_OR_HAS_ACTION(element))
8858   {
8859     // copy element change control values to new field
8860     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8861     ChangePage[newx][newy]  = ChangePage[x][y];
8862     ChangeCount[newx][newy] = ChangeCount[x][y];
8863     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8864   }
8865
8866   CustomValue[newx][newy] = CustomValue[x][y];
8867
8868   ChangeDelay[x][y] = 0;
8869   ChangePage[x][y] = -1;
8870   ChangeCount[x][y] = 0;
8871   ChangeEvent[x][y] = -1;
8872
8873   CustomValue[x][y] = 0;
8874
8875   // copy animation control values to new field
8876   GfxFrame[newx][newy]  = GfxFrame[x][y];
8877   GfxRandom[newx][newy] = GfxRandom[x][y];      // keep same random value
8878   GfxAction[newx][newy] = GfxAction[x][y];      // keep action one frame
8879   GfxDir[newx][newy]    = GfxDir[x][y];         // keep element direction
8880
8881   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8882
8883   // some elements can leave other elements behind after moving
8884   if (ei->move_leave_element != EL_EMPTY &&
8885       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8886       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8887   {
8888     int move_leave_element = ei->move_leave_element;
8889
8890     // this makes it possible to leave the removed element again
8891     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8892       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8893
8894     Tile[x][y] = move_leave_element;
8895
8896     if (element_info[Tile[x][y]].move_direction_initial == MV_START_PREVIOUS)
8897       MovDir[x][y] = direction;
8898
8899     InitField(x, y, FALSE);
8900
8901     if (GFX_CRUMBLED(Tile[x][y]))
8902       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8903
8904     if (IS_PLAYER_ELEMENT(move_leave_element))
8905       RelocatePlayer(x, y, move_leave_element);
8906   }
8907
8908   // do this after checking for left-behind element
8909   ResetGfxAnimation(x, y);      // reset animation values for old field
8910
8911   if (!CAN_MOVE(element) ||
8912       (CAN_FALL(element) && direction == MV_DOWN &&
8913        (element == EL_SPRING ||
8914         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8915         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8916     GfxDir[x][y] = MovDir[newx][newy] = 0;
8917
8918   TEST_DrawLevelField(x, y);
8919   TEST_DrawLevelField(newx, newy);
8920
8921   Stop[newx][newy] = TRUE;      // ignore this element until the next frame
8922
8923   // prevent pushed element from moving on in pushed direction
8924   if (pushed_by_player && CAN_MOVE(element) &&
8925       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8926       !(element_info[element].move_pattern & direction))
8927     TurnRound(newx, newy);
8928
8929   // prevent elements on conveyor belt from moving on in last direction
8930   if (pushed_by_conveyor && CAN_FALL(element) &&
8931       direction & MV_HORIZONTAL)
8932     MovDir[newx][newy] = 0;
8933
8934   if (!pushed_by_player)
8935   {
8936     int nextx = newx + dx, nexty = newy + dy;
8937     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8938
8939     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8940
8941     if (CAN_FALL(element) && direction == MV_DOWN)
8942       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8943
8944     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8945       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8946
8947     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8948       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8949   }
8950
8951   if (DONT_TOUCH(element))      // object may be nasty to player or others
8952   {
8953     TestIfBadThingTouchesPlayer(newx, newy);
8954     TestIfBadThingTouchesFriend(newx, newy);
8955
8956     if (!IS_CUSTOM_ELEMENT(element))
8957       TestIfBadThingTouchesOtherBadThing(newx, newy);
8958   }
8959   else if (element == EL_PENGUIN)
8960     TestIfFriendTouchesBadThing(newx, newy);
8961
8962   if (DONT_GET_HIT_BY(element))
8963   {
8964     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8965   }
8966
8967   // give the player one last chance (one more frame) to move away
8968   if (CAN_FALL(element) && direction == MV_DOWN &&
8969       (last_line || (!IS_FREE(x, newy + 1) &&
8970                      (!IS_PLAYER(x, newy + 1) ||
8971                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8972     Impact(x, newy);
8973
8974   if (pushed_by_player && !game.use_change_when_pushing_bug)
8975   {
8976     int push_side = MV_DIR_OPPOSITE(direction);
8977     struct PlayerInfo *player = PLAYERINFO(x, y);
8978
8979     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8980                                player->index_bit, push_side);
8981     CheckTriggeredElementChangeByPlayer(newx, newy, element, CE_PLAYER_PUSHES_X,
8982                                         player->index_bit, push_side);
8983   }
8984
8985   if (element == EL_EMC_ANDROID && pushed_by_player)    // make another move
8986     MovDelay[newx][newy] = 1;
8987
8988   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8989
8990   TestIfElementTouchesCustomElement(x, y);      // empty or new element
8991   TestIfElementHitsCustomElement(newx, newy, direction);
8992   TestIfPlayerTouchesCustomElement(newx, newy);
8993   TestIfElementTouchesCustomElement(newx, newy);
8994
8995   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8996       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8997     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8998                              MV_DIR_OPPOSITE(direction));
8999 }
9000
9001 int AmoebaNeighbourNr(int ax, int ay)
9002 {
9003   int i;
9004   int element = Tile[ax][ay];
9005   int group_nr = 0;
9006   struct XY *xy = xy_topdown;
9007
9008   for (i = 0; i < NUM_DIRECTIONS; i++)
9009   {
9010     int x = ax + xy[i].x;
9011     int y = ay + xy[i].y;
9012
9013     if (!IN_LEV_FIELD(x, y))
9014       continue;
9015
9016     if (Tile[x][y] == element && AmoebaNr[x][y] > 0)
9017       group_nr = AmoebaNr[x][y];
9018   }
9019
9020   return group_nr;
9021 }
9022
9023 static void AmoebaMerge(int ax, int ay)
9024 {
9025   int i, x, y, xx, yy;
9026   int new_group_nr = AmoebaNr[ax][ay];
9027   struct XY *xy = xy_topdown;
9028
9029   if (new_group_nr == 0)
9030     return;
9031
9032   for (i = 0; i < NUM_DIRECTIONS; i++)
9033   {
9034     x = ax + xy[i].x;
9035     y = ay + xy[i].y;
9036
9037     if (!IN_LEV_FIELD(x, y))
9038       continue;
9039
9040     if ((Tile[x][y] == EL_AMOEBA_FULL ||
9041          Tile[x][y] == EL_BD_AMOEBA ||
9042          Tile[x][y] == EL_AMOEBA_DEAD) &&
9043         AmoebaNr[x][y] != new_group_nr)
9044     {
9045       int old_group_nr = AmoebaNr[x][y];
9046
9047       if (old_group_nr == 0)
9048         return;
9049
9050       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
9051       AmoebaCnt[old_group_nr] = 0;
9052       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
9053       AmoebaCnt2[old_group_nr] = 0;
9054
9055       SCAN_PLAYFIELD(xx, yy)
9056       {
9057         if (AmoebaNr[xx][yy] == old_group_nr)
9058           AmoebaNr[xx][yy] = new_group_nr;
9059       }
9060     }
9061   }
9062 }
9063
9064 void AmoebaToDiamond(int ax, int ay)
9065 {
9066   int i, x, y;
9067
9068   if (Tile[ax][ay] == EL_AMOEBA_DEAD)
9069   {
9070     int group_nr = AmoebaNr[ax][ay];
9071
9072 #ifdef DEBUG
9073     if (group_nr == 0)
9074     {
9075       Debug("game:playing:AmoebaToDiamond", "ax = %d, ay = %d", ax, ay);
9076       Debug("game:playing:AmoebaToDiamond", "This should never happen!");
9077
9078       return;
9079     }
9080 #endif
9081
9082     SCAN_PLAYFIELD(x, y)
9083     {
9084       if (Tile[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9085       {
9086         AmoebaNr[x][y] = 0;
9087         Tile[x][y] = EL_AMOEBA_TO_DIAMOND;
9088       }
9089     }
9090
9091     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9092                             SND_AMOEBA_TURNING_TO_GEM :
9093                             SND_AMOEBA_TURNING_TO_ROCK));
9094     Bang(ax, ay);
9095   }
9096   else
9097   {
9098     struct XY *xy = xy_topdown;
9099
9100     for (i = 0; i < NUM_DIRECTIONS; i++)
9101     {
9102       x = ax + xy[i].x;
9103       y = ay + xy[i].y;
9104
9105       if (!IN_LEV_FIELD(x, y))
9106         continue;
9107
9108       if (Tile[x][y] == EL_AMOEBA_TO_DIAMOND)
9109       {
9110         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9111                               SND_AMOEBA_TURNING_TO_GEM :
9112                               SND_AMOEBA_TURNING_TO_ROCK));
9113         Bang(x, y);
9114       }
9115     }
9116   }
9117 }
9118
9119 static void AmoebaToDiamondBD(int ax, int ay, int new_element)
9120 {
9121   int x, y;
9122   int group_nr = AmoebaNr[ax][ay];
9123   boolean done = FALSE;
9124
9125 #ifdef DEBUG
9126   if (group_nr == 0)
9127   {
9128     Debug("game:playing:AmoebaToDiamondBD", "ax = %d, ay = %d", ax, ay);
9129     Debug("game:playing:AmoebaToDiamondBD", "This should never happen!");
9130
9131     return;
9132   }
9133 #endif
9134
9135   SCAN_PLAYFIELD(x, y)
9136   {
9137     if (AmoebaNr[x][y] == group_nr &&
9138         (Tile[x][y] == EL_AMOEBA_DEAD ||
9139          Tile[x][y] == EL_BD_AMOEBA ||
9140          Tile[x][y] == EL_AMOEBA_GROWING))
9141     {
9142       AmoebaNr[x][y] = 0;
9143       Tile[x][y] = new_element;
9144       InitField(x, y, FALSE);
9145       TEST_DrawLevelField(x, y);
9146       done = TRUE;
9147     }
9148   }
9149
9150   if (done)
9151     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9152                             SND_BD_AMOEBA_TURNING_TO_ROCK :
9153                             SND_BD_AMOEBA_TURNING_TO_GEM));
9154 }
9155
9156 static void AmoebaGrowing(int x, int y)
9157 {
9158   static DelayCounter sound_delay = { 0 };
9159
9160   if (!MovDelay[x][y])          // start new growing cycle
9161   {
9162     MovDelay[x][y] = 7;
9163
9164     if (DelayReached(&sound_delay))
9165     {
9166       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9167       sound_delay.value = 30;
9168     }
9169   }
9170
9171   if (MovDelay[x][y])           // wait some time before growing bigger
9172   {
9173     MovDelay[x][y]--;
9174     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9175     {
9176       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9177                                            6 - MovDelay[x][y]);
9178
9179       DrawLevelGraphic(x, y, IMG_AMOEBA_GROWING, frame);
9180     }
9181
9182     if (!MovDelay[x][y])
9183     {
9184       Tile[x][y] = Store[x][y];
9185       Store[x][y] = 0;
9186       TEST_DrawLevelField(x, y);
9187     }
9188   }
9189 }
9190
9191 static void AmoebaShrinking(int x, int y)
9192 {
9193   static DelayCounter sound_delay = { 0 };
9194
9195   if (!MovDelay[x][y])          // start new shrinking cycle
9196   {
9197     MovDelay[x][y] = 7;
9198
9199     if (DelayReached(&sound_delay))
9200       sound_delay.value = 30;
9201   }
9202
9203   if (MovDelay[x][y])           // wait some time before shrinking
9204   {
9205     MovDelay[x][y]--;
9206     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9207     {
9208       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9209                                            6 - MovDelay[x][y]);
9210
9211       DrawLevelGraphic(x, y, IMG_AMOEBA_SHRINKING, frame);
9212     }
9213
9214     if (!MovDelay[x][y])
9215     {
9216       Tile[x][y] = EL_EMPTY;
9217       TEST_DrawLevelField(x, y);
9218
9219       // don't let mole enter this field in this cycle;
9220       // (give priority to objects falling to this field from above)
9221       Stop[x][y] = TRUE;
9222     }
9223   }
9224 }
9225
9226 static void AmoebaReproduce(int ax, int ay)
9227 {
9228   int i;
9229   int element = Tile[ax][ay];
9230   int graphic = el2img(element);
9231   int newax = ax, neway = ay;
9232   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9233   struct XY *xy = xy_topdown;
9234
9235   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9236   {
9237     Tile[ax][ay] = EL_AMOEBA_DEAD;
9238     TEST_DrawLevelField(ax, ay);
9239     return;
9240   }
9241
9242   if (IS_ANIMATED(graphic))
9243     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9244
9245   if (!MovDelay[ax][ay])        // start making new amoeba field
9246     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9247
9248   if (MovDelay[ax][ay])         // wait some time before making new amoeba
9249   {
9250     MovDelay[ax][ay]--;
9251     if (MovDelay[ax][ay])
9252       return;
9253   }
9254
9255   if (can_drop)                 // EL_AMOEBA_WET or EL_EMC_DRIPPER
9256   {
9257     int start = RND(4);
9258     int x = ax + xy[start].x;
9259     int y = ay + xy[start].y;
9260
9261     if (!IN_LEV_FIELD(x, y))
9262       return;
9263
9264     if (IS_FREE(x, y) ||
9265         CAN_GROW_INTO(Tile[x][y]) ||
9266         Tile[x][y] == EL_QUICKSAND_EMPTY ||
9267         Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9268     {
9269       newax = x;
9270       neway = y;
9271     }
9272
9273     if (newax == ax && neway == ay)
9274       return;
9275   }
9276   else                          // normal or "filled" (BD style) amoeba
9277   {
9278     int start = RND(4);
9279     boolean waiting_for_player = FALSE;
9280
9281     for (i = 0; i < NUM_DIRECTIONS; i++)
9282     {
9283       int j = (start + i) % 4;
9284       int x = ax + xy[j].x;
9285       int y = ay + xy[j].y;
9286
9287       if (!IN_LEV_FIELD(x, y))
9288         continue;
9289
9290       if (IS_FREE(x, y) ||
9291           CAN_GROW_INTO(Tile[x][y]) ||
9292           Tile[x][y] == EL_QUICKSAND_EMPTY ||
9293           Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9294       {
9295         newax = x;
9296         neway = y;
9297         break;
9298       }
9299       else if (IS_PLAYER(x, y))
9300         waiting_for_player = TRUE;
9301     }
9302
9303     if (newax == ax && neway == ay)             // amoeba cannot grow
9304     {
9305       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9306       {
9307         Tile[ax][ay] = EL_AMOEBA_DEAD;
9308         TEST_DrawLevelField(ax, ay);
9309         AmoebaCnt[AmoebaNr[ax][ay]]--;
9310
9311         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   // amoeba is completely dead
9312         {
9313           if (element == EL_AMOEBA_FULL)
9314             AmoebaToDiamond(ax, ay);
9315           else if (element == EL_BD_AMOEBA)
9316             AmoebaToDiamondBD(ax, ay, level.amoeba_content);
9317         }
9318       }
9319       return;
9320     }
9321     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9322     {
9323       // amoeba gets larger by growing in some direction
9324
9325       int new_group_nr = AmoebaNr[ax][ay];
9326
9327 #ifdef DEBUG
9328   if (new_group_nr == 0)
9329   {
9330     Debug("game:playing:AmoebaReproduce", "newax = %d, neway = %d",
9331           newax, neway);
9332     Debug("game:playing:AmoebaReproduce", "This should never happen!");
9333
9334     return;
9335   }
9336 #endif
9337
9338       AmoebaNr[newax][neway] = new_group_nr;
9339       AmoebaCnt[new_group_nr]++;
9340       AmoebaCnt2[new_group_nr]++;
9341
9342       // if amoeba touches other amoeba(s) after growing, unify them
9343       AmoebaMerge(newax, neway);
9344
9345       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9346       {
9347         AmoebaToDiamondBD(newax, neway, EL_BD_ROCK);
9348         return;
9349       }
9350     }
9351   }
9352
9353   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9354       (neway == lev_fieldy - 1 && newax != ax))
9355   {
9356     Tile[newax][neway] = EL_AMOEBA_GROWING;     // creation of new amoeba
9357     Store[newax][neway] = element;
9358   }
9359   else if (neway == ay || element == EL_EMC_DRIPPER)
9360   {
9361     Tile[newax][neway] = EL_AMOEBA_DROP;        // drop left/right of amoeba
9362
9363     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9364   }
9365   else
9366   {
9367     InitMovingField(ax, ay, MV_DOWN);           // drop dripping from amoeba
9368     Tile[ax][ay] = EL_AMOEBA_DROPPING;
9369     Store[ax][ay] = EL_AMOEBA_DROP;
9370     ContinueMoving(ax, ay);
9371     return;
9372   }
9373
9374   TEST_DrawLevelField(newax, neway);
9375 }
9376
9377 static void Life(int ax, int ay)
9378 {
9379   int x1, y1, x2, y2;
9380   int life_time = 40;
9381   int element = Tile[ax][ay];
9382   int graphic = el2img(element);
9383   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9384                          level.biomaze);
9385   boolean changed = FALSE;
9386
9387   if (IS_ANIMATED(graphic))
9388     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9389
9390   if (Stop[ax][ay])
9391     return;
9392
9393   if (!MovDelay[ax][ay])        // start new "game of life" cycle
9394     MovDelay[ax][ay] = life_time;
9395
9396   if (MovDelay[ax][ay])         // wait some time before next cycle
9397   {
9398     MovDelay[ax][ay]--;
9399     if (MovDelay[ax][ay])
9400       return;
9401   }
9402
9403   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9404   {
9405     int xx = ax+x1, yy = ay+y1;
9406     int old_element = Tile[xx][yy];
9407     int num_neighbours = 0;
9408
9409     if (!IN_LEV_FIELD(xx, yy))
9410       continue;
9411
9412     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9413     {
9414       int x = xx+x2, y = yy+y2;
9415
9416       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9417         continue;
9418
9419       boolean is_player_cell = (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y));
9420       boolean is_neighbour = FALSE;
9421
9422       if (level.use_life_bugs)
9423         is_neighbour =
9424           (((Tile[x][y] == element || is_player_cell) && !Stop[x][y]) ||
9425            (IS_FREE(x, y)                             &&  Stop[x][y]));
9426       else
9427         is_neighbour =
9428           (Last[x][y] == element || is_player_cell);
9429
9430       if (is_neighbour)
9431         num_neighbours++;
9432     }
9433
9434     boolean is_free = FALSE;
9435
9436     if (level.use_life_bugs)
9437       is_free = (IS_FREE(xx, yy));
9438     else
9439       is_free = (IS_FREE(xx, yy) && Last[xx][yy] == EL_EMPTY);
9440
9441     if (xx == ax && yy == ay)           // field in the middle
9442     {
9443       if (num_neighbours < life_parameter[0] ||
9444           num_neighbours > life_parameter[1])
9445       {
9446         Tile[xx][yy] = EL_EMPTY;
9447         if (Tile[xx][yy] != old_element)
9448           TEST_DrawLevelField(xx, yy);
9449         Stop[xx][yy] = TRUE;
9450         changed = TRUE;
9451       }
9452     }
9453     else if (is_free || CAN_GROW_INTO(Tile[xx][yy]))
9454     {                                   // free border field
9455       if (num_neighbours >= life_parameter[2] &&
9456           num_neighbours <= life_parameter[3])
9457       {
9458         Tile[xx][yy] = element;
9459         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time - 1);
9460         if (Tile[xx][yy] != old_element)
9461           TEST_DrawLevelField(xx, yy);
9462         Stop[xx][yy] = TRUE;
9463         changed = TRUE;
9464       }
9465     }
9466   }
9467
9468   if (changed)
9469     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9470                    SND_GAME_OF_LIFE_GROWING);
9471 }
9472
9473 static void InitRobotWheel(int x, int y)
9474 {
9475   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9476 }
9477
9478 static void RunRobotWheel(int x, int y)
9479 {
9480   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9481 }
9482
9483 static void StopRobotWheel(int x, int y)
9484 {
9485   if (game.robot_wheel_x == x &&
9486       game.robot_wheel_y == y)
9487   {
9488     game.robot_wheel_x = -1;
9489     game.robot_wheel_y = -1;
9490     game.robot_wheel_active = FALSE;
9491   }
9492 }
9493
9494 static void InitTimegateWheel(int x, int y)
9495 {
9496   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9497 }
9498
9499 static void RunTimegateWheel(int x, int y)
9500 {
9501   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9502 }
9503
9504 static void InitMagicBallDelay(int x, int y)
9505 {
9506   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9507 }
9508
9509 static void ActivateMagicBall(int bx, int by)
9510 {
9511   int x, y;
9512
9513   if (level.ball_random)
9514   {
9515     int pos_border = RND(8);    // select one of the eight border elements
9516     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9517     int xx = pos_content % 3;
9518     int yy = pos_content / 3;
9519
9520     x = bx - 1 + xx;
9521     y = by - 1 + yy;
9522
9523     if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9524       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9525   }
9526   else
9527   {
9528     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9529     {
9530       int xx = x - bx + 1;
9531       int yy = y - by + 1;
9532
9533       if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9534         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9535     }
9536   }
9537
9538   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9539 }
9540
9541 static void CheckExit(int x, int y)
9542 {
9543   if (game.gems_still_needed > 0 ||
9544       game.sokoban_fields_still_needed > 0 ||
9545       game.sokoban_objects_still_needed > 0 ||
9546       game.lights_still_needed > 0)
9547   {
9548     int element = Tile[x][y];
9549     int graphic = el2img(element);
9550
9551     if (IS_ANIMATED(graphic))
9552       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9553
9554     return;
9555   }
9556
9557   // do not re-open exit door closed after last player
9558   if (game.all_players_gone)
9559     return;
9560
9561   Tile[x][y] = EL_EXIT_OPENING;
9562
9563   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9564 }
9565
9566 static void CheckExitEM(int x, int y)
9567 {
9568   if (game.gems_still_needed > 0 ||
9569       game.sokoban_fields_still_needed > 0 ||
9570       game.sokoban_objects_still_needed > 0 ||
9571       game.lights_still_needed > 0)
9572   {
9573     int element = Tile[x][y];
9574     int graphic = el2img(element);
9575
9576     if (IS_ANIMATED(graphic))
9577       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9578
9579     return;
9580   }
9581
9582   // do not re-open exit door closed after last player
9583   if (game.all_players_gone)
9584     return;
9585
9586   Tile[x][y] = EL_EM_EXIT_OPENING;
9587
9588   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9589 }
9590
9591 static void CheckExitSteel(int x, int y)
9592 {
9593   if (game.gems_still_needed > 0 ||
9594       game.sokoban_fields_still_needed > 0 ||
9595       game.sokoban_objects_still_needed > 0 ||
9596       game.lights_still_needed > 0)
9597   {
9598     int element = Tile[x][y];
9599     int graphic = el2img(element);
9600
9601     if (IS_ANIMATED(graphic))
9602       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9603
9604     return;
9605   }
9606
9607   // do not re-open exit door closed after last player
9608   if (game.all_players_gone)
9609     return;
9610
9611   Tile[x][y] = EL_STEEL_EXIT_OPENING;
9612
9613   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9614 }
9615
9616 static void CheckExitSteelEM(int x, int y)
9617 {
9618   if (game.gems_still_needed > 0 ||
9619       game.sokoban_fields_still_needed > 0 ||
9620       game.sokoban_objects_still_needed > 0 ||
9621       game.lights_still_needed > 0)
9622   {
9623     int element = Tile[x][y];
9624     int graphic = el2img(element);
9625
9626     if (IS_ANIMATED(graphic))
9627       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9628
9629     return;
9630   }
9631
9632   // do not re-open exit door closed after last player
9633   if (game.all_players_gone)
9634     return;
9635
9636   Tile[x][y] = EL_EM_STEEL_EXIT_OPENING;
9637
9638   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9639 }
9640
9641 static void CheckExitSP(int x, int y)
9642 {
9643   if (game.gems_still_needed > 0)
9644   {
9645     int element = Tile[x][y];
9646     int graphic = el2img(element);
9647
9648     if (IS_ANIMATED(graphic))
9649       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9650
9651     return;
9652   }
9653
9654   // do not re-open exit door closed after last player
9655   if (game.all_players_gone)
9656     return;
9657
9658   Tile[x][y] = EL_SP_EXIT_OPENING;
9659
9660   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9661 }
9662
9663 static void CloseAllOpenTimegates(void)
9664 {
9665   int x, y;
9666
9667   SCAN_PLAYFIELD(x, y)
9668   {
9669     int element = Tile[x][y];
9670
9671     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9672     {
9673       Tile[x][y] = EL_TIMEGATE_CLOSING;
9674
9675       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9676     }
9677   }
9678 }
9679
9680 static void DrawTwinkleOnField(int x, int y)
9681 {
9682   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9683     return;
9684
9685   if (Tile[x][y] == EL_BD_DIAMOND)
9686     return;
9687
9688   if (MovDelay[x][y] == 0)      // next animation frame
9689     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9690
9691   if (MovDelay[x][y] != 0)      // wait some time before next frame
9692   {
9693     MovDelay[x][y]--;
9694
9695     DrawLevelElementAnimation(x, y, Tile[x][y]);
9696
9697     if (MovDelay[x][y] != 0)
9698     {
9699       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9700                                            10 - MovDelay[x][y]);
9701
9702       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9703     }
9704   }
9705 }
9706
9707 static void WallGrowing(int x, int y)
9708 {
9709   int delay = 6;
9710
9711   if (!MovDelay[x][y])          // next animation frame
9712     MovDelay[x][y] = 3 * delay;
9713
9714   if (MovDelay[x][y])           // wait some time before next frame
9715   {
9716     MovDelay[x][y]--;
9717
9718     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9719     {
9720       int graphic = el_dir2img(Tile[x][y], GfxDir[x][y]);
9721       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9722
9723       DrawLevelGraphic(x, y, graphic, frame);
9724     }
9725
9726     if (!MovDelay[x][y])
9727     {
9728       if (MovDir[x][y] == MV_LEFT)
9729       {
9730         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Tile[x - 1][y]))
9731           TEST_DrawLevelField(x - 1, y);
9732       }
9733       else if (MovDir[x][y] == MV_RIGHT)
9734       {
9735         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Tile[x + 1][y]))
9736           TEST_DrawLevelField(x + 1, y);
9737       }
9738       else if (MovDir[x][y] == MV_UP)
9739       {
9740         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Tile[x][y - 1]))
9741           TEST_DrawLevelField(x, y - 1);
9742       }
9743       else
9744       {
9745         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Tile[x][y + 1]))
9746           TEST_DrawLevelField(x, y + 1);
9747       }
9748
9749       Tile[x][y] = Store[x][y];
9750       Store[x][y] = 0;
9751       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9752       TEST_DrawLevelField(x, y);
9753     }
9754   }
9755 }
9756
9757 static void CheckWallGrowing(int ax, int ay)
9758 {
9759   int element = Tile[ax][ay];
9760   int graphic = el2img(element);
9761   boolean free_top    = FALSE;
9762   boolean free_bottom = FALSE;
9763   boolean free_left   = FALSE;
9764   boolean free_right  = FALSE;
9765   boolean stop_top    = FALSE;
9766   boolean stop_bottom = FALSE;
9767   boolean stop_left   = FALSE;
9768   boolean stop_right  = FALSE;
9769   boolean new_wall    = FALSE;
9770
9771   boolean is_steelwall  = (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9772                            element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9773                            element == EL_EXPANDABLE_STEELWALL_ANY);
9774
9775   boolean grow_vertical   = (element == EL_EXPANDABLE_WALL_VERTICAL ||
9776                              element == EL_EXPANDABLE_WALL_ANY ||
9777                              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9778                              element == EL_EXPANDABLE_STEELWALL_ANY);
9779
9780   boolean grow_horizontal = (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9781                              element == EL_EXPANDABLE_WALL_ANY ||
9782                              element == EL_EXPANDABLE_WALL ||
9783                              element == EL_BD_EXPANDABLE_WALL ||
9784                              element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9785                              element == EL_EXPANDABLE_STEELWALL_ANY);
9786
9787   boolean stop_vertical   = (element == EL_EXPANDABLE_WALL_VERTICAL ||
9788                              element == EL_EXPANDABLE_STEELWALL_VERTICAL);
9789
9790   boolean stop_horizontal = (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9791                              element == EL_EXPANDABLE_WALL ||
9792                              element == EL_EXPANDABLE_STEELWALL_HORIZONTAL);
9793
9794   int wall_growing = (is_steelwall ?
9795                       EL_EXPANDABLE_STEELWALL_GROWING :
9796                       EL_EXPANDABLE_WALL_GROWING);
9797
9798   int gfx_wall_growing_up    = (is_steelwall ?
9799                                 IMG_EXPANDABLE_STEELWALL_GROWING_UP :
9800                                 IMG_EXPANDABLE_WALL_GROWING_UP);
9801   int gfx_wall_growing_down  = (is_steelwall ?
9802                                 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN :
9803                                 IMG_EXPANDABLE_WALL_GROWING_DOWN);
9804   int gfx_wall_growing_left  = (is_steelwall ?
9805                                 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT :
9806                                 IMG_EXPANDABLE_WALL_GROWING_LEFT);
9807   int gfx_wall_growing_right = (is_steelwall ?
9808                                 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT :
9809                                 IMG_EXPANDABLE_WALL_GROWING_RIGHT);
9810
9811   if (IS_ANIMATED(graphic))
9812     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9813
9814   if (!MovDelay[ax][ay])        // start building new wall
9815     MovDelay[ax][ay] = 6;
9816
9817   if (MovDelay[ax][ay])         // wait some time before building new wall
9818   {
9819     MovDelay[ax][ay]--;
9820     if (MovDelay[ax][ay])
9821       return;
9822   }
9823
9824   if (IN_LEV_FIELD(ax, ay - 1) && IS_FREE(ax, ay - 1))
9825     free_top = TRUE;
9826   if (IN_LEV_FIELD(ax, ay + 1) && IS_FREE(ax, ay + 1))
9827     free_bottom = TRUE;
9828   if (IN_LEV_FIELD(ax - 1, ay) && IS_FREE(ax - 1, ay))
9829     free_left = TRUE;
9830   if (IN_LEV_FIELD(ax + 1, ay) && IS_FREE(ax + 1, ay))
9831     free_right = TRUE;
9832
9833   if (grow_vertical)
9834   {
9835     if (free_top)
9836     {
9837       Tile[ax][ay - 1] = wall_growing;
9838       Store[ax][ay - 1] = element;
9839       GfxDir[ax][ay - 1] = MovDir[ax][ay - 1] = MV_UP;
9840
9841       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay - 1)))
9842         DrawLevelGraphic(ax, ay - 1, gfx_wall_growing_up, 0);
9843
9844       new_wall = TRUE;
9845     }
9846
9847     if (free_bottom)
9848     {
9849       Tile[ax][ay + 1] = wall_growing;
9850       Store[ax][ay + 1] = element;
9851       GfxDir[ax][ay + 1] = MovDir[ax][ay + 1] = MV_DOWN;
9852
9853       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay + 1)))
9854         DrawLevelGraphic(ax, ay + 1, gfx_wall_growing_down, 0);
9855
9856       new_wall = TRUE;
9857     }
9858   }
9859
9860   if (grow_horizontal)
9861   {
9862     if (free_left)
9863     {
9864       Tile[ax - 1][ay] = wall_growing;
9865       Store[ax - 1][ay] = element;
9866       GfxDir[ax - 1][ay] = MovDir[ax - 1][ay] = MV_LEFT;
9867
9868       if (IN_SCR_FIELD(SCREENX(ax - 1), SCREENY(ay)))
9869         DrawLevelGraphic(ax - 1, ay, gfx_wall_growing_left, 0);
9870
9871       new_wall = TRUE;
9872     }
9873
9874     if (free_right)
9875     {
9876       Tile[ax + 1][ay] = wall_growing;
9877       Store[ax + 1][ay] = element;
9878       GfxDir[ax + 1][ay] = MovDir[ax + 1][ay] = MV_RIGHT;
9879
9880       if (IN_SCR_FIELD(SCREENX(ax + 1), SCREENY(ay)))
9881         DrawLevelGraphic(ax + 1, ay, gfx_wall_growing_right, 0);
9882
9883       new_wall = TRUE;
9884     }
9885   }
9886
9887   if (element == EL_EXPANDABLE_WALL && (free_left || free_right))
9888     TEST_DrawLevelField(ax, ay);
9889
9890   if (!IN_LEV_FIELD(ax, ay - 1) || IS_WALL(Tile[ax][ay - 1]))
9891     stop_top = TRUE;
9892   if (!IN_LEV_FIELD(ax, ay + 1) || IS_WALL(Tile[ax][ay + 1]))
9893     stop_bottom = TRUE;
9894   if (!IN_LEV_FIELD(ax - 1, ay) || IS_WALL(Tile[ax - 1][ay]))
9895     stop_left = TRUE;
9896   if (!IN_LEV_FIELD(ax + 1, ay) || IS_WALL(Tile[ax + 1][ay]))
9897     stop_right = TRUE;
9898
9899   if (((stop_top && stop_bottom) || stop_horizontal) &&
9900       ((stop_left && stop_right) || stop_vertical))
9901     Tile[ax][ay] = (is_steelwall ? EL_STEELWALL : EL_WALL);
9902
9903   if (new_wall)
9904     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9905 }
9906
9907 static void CheckForDragon(int x, int y)
9908 {
9909   int i, j;
9910   boolean dragon_found = FALSE;
9911   struct XY *xy = xy_topdown;
9912
9913   for (i = 0; i < NUM_DIRECTIONS; i++)
9914   {
9915     for (j = 0; j < 4; j++)
9916     {
9917       int xx = x + j * xy[i].x;
9918       int yy = y + j * xy[i].y;
9919
9920       if (IN_LEV_FIELD(xx, yy) &&
9921           (Tile[xx][yy] == EL_FLAMES || Tile[xx][yy] == EL_DRAGON))
9922       {
9923         if (Tile[xx][yy] == EL_DRAGON)
9924           dragon_found = TRUE;
9925       }
9926       else
9927         break;
9928     }
9929   }
9930
9931   if (!dragon_found)
9932   {
9933     for (i = 0; i < NUM_DIRECTIONS; i++)
9934     {
9935       for (j = 0; j < 3; j++)
9936       {
9937         int xx = x + j * xy[i].x;
9938         int yy = y + j * xy[i].y;
9939
9940         if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == EL_FLAMES)
9941         {
9942           Tile[xx][yy] = EL_EMPTY;
9943           TEST_DrawLevelField(xx, yy);
9944         }
9945         else
9946           break;
9947       }
9948     }
9949   }
9950 }
9951
9952 static void InitBuggyBase(int x, int y)
9953 {
9954   int element = Tile[x][y];
9955   int activating_delay = FRAMES_PER_SECOND / 4;
9956
9957   ChangeDelay[x][y] =
9958     (element == EL_SP_BUGGY_BASE ?
9959      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9960      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9961      activating_delay :
9962      element == EL_SP_BUGGY_BASE_ACTIVE ?
9963      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9964 }
9965
9966 static void WarnBuggyBase(int x, int y)
9967 {
9968   int i;
9969   struct XY *xy = xy_topdown;
9970
9971   for (i = 0; i < NUM_DIRECTIONS; i++)
9972   {
9973     int xx = x + xy[i].x;
9974     int yy = y + xy[i].y;
9975
9976     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9977     {
9978       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9979
9980       break;
9981     }
9982   }
9983 }
9984
9985 static void InitTrap(int x, int y)
9986 {
9987   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9988 }
9989
9990 static void ActivateTrap(int x, int y)
9991 {
9992   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9993 }
9994
9995 static void ChangeActiveTrap(int x, int y)
9996 {
9997   int graphic = IMG_TRAP_ACTIVE;
9998
9999   // if new animation frame was drawn, correct crumbled sand border
10000   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
10001     TEST_DrawLevelFieldCrumbled(x, y);
10002 }
10003
10004 static int getSpecialActionElement(int element, int number, int base_element)
10005 {
10006   return (element != EL_EMPTY ? element :
10007           number != -1 ? base_element + number - 1 :
10008           EL_EMPTY);
10009 }
10010
10011 static int getModifiedActionNumber(int value_old, int operator, int operand,
10012                                    int value_min, int value_max)
10013 {
10014   int value_new = (operator == CA_MODE_SET      ? operand :
10015                    operator == CA_MODE_ADD      ? value_old + operand :
10016                    operator == CA_MODE_SUBTRACT ? value_old - operand :
10017                    operator == CA_MODE_MULTIPLY ? value_old * operand :
10018                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
10019                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
10020                    value_old);
10021
10022   return (value_new < value_min ? value_min :
10023           value_new > value_max ? value_max :
10024           value_new);
10025 }
10026
10027 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10028 {
10029   struct ElementInfo *ei = &element_info[element];
10030   struct ElementChangeInfo *change = &ei->change_page[page];
10031   int target_element = change->target_element;
10032   int action_type = change->action_type;
10033   int action_mode = change->action_mode;
10034   int action_arg = change->action_arg;
10035   int action_element = change->action_element;
10036   int i;
10037
10038   if (!change->has_action)
10039     return;
10040
10041   // ---------- determine action paramater values -----------------------------
10042
10043   int level_time_value =
10044     (level.time > 0 ? TimeLeft :
10045      TimePlayed);
10046
10047   int action_arg_element_raw =
10048     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
10049      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10050      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
10051      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
10052      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10053      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
10054      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
10055      EL_EMPTY);
10056   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10057
10058   int action_arg_direction =
10059     (action_arg >= CA_ARG_DIRECTION_LEFT &&
10060      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10061      action_arg == CA_ARG_DIRECTION_TRIGGER ?
10062      change->actual_trigger_side :
10063      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10064      MV_DIR_OPPOSITE(change->actual_trigger_side) :
10065      MV_NONE);
10066
10067   int action_arg_number_min =
10068     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10069      CA_ARG_MIN);
10070
10071   int action_arg_number_max =
10072     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10073      action_type == CA_SET_LEVEL_GEMS ? 999 :
10074      action_type == CA_SET_LEVEL_TIME ? 9999 :
10075      action_type == CA_SET_LEVEL_SCORE ? 99999 :
10076      action_type == CA_SET_CE_VALUE ? 9999 :
10077      action_type == CA_SET_CE_SCORE ? 9999 :
10078      CA_ARG_MAX);
10079
10080   int action_arg_number_reset =
10081     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10082      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10083      action_type == CA_SET_LEVEL_TIME ? level.time :
10084      action_type == CA_SET_LEVEL_SCORE ? 0 :
10085      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10086      action_type == CA_SET_CE_SCORE ? 0 :
10087      0);
10088
10089   int action_arg_number =
10090     (action_arg <= CA_ARG_MAX ? action_arg :
10091      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10092      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10093      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10094      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10095      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10096      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10097      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10098      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10099      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10100      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10101      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? game.gems_still_needed :
10102      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? game.score :
10103      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10104      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10105      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10106      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10107      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10108      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10109      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
10110      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10111      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
10112      -1);
10113
10114   int action_arg_number_old =
10115     (action_type == CA_SET_LEVEL_GEMS ? game.gems_still_needed :
10116      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10117      action_type == CA_SET_LEVEL_SCORE ? game.score :
10118      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10119      action_type == CA_SET_CE_SCORE ? ei->collect_score :
10120      0);
10121
10122   int action_arg_number_new =
10123     getModifiedActionNumber(action_arg_number_old,
10124                             action_mode, action_arg_number,
10125                             action_arg_number_min, action_arg_number_max);
10126
10127   int trigger_player_bits =
10128     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10129      change->actual_trigger_player_bits : change->trigger_player);
10130
10131   int action_arg_player_bits =
10132     (action_arg >= CA_ARG_PLAYER_1 &&
10133      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10134      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10135      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10136      PLAYER_BITS_ANY);
10137
10138   // ---------- execute action  -----------------------------------------------
10139
10140   switch (action_type)
10141   {
10142     case CA_NO_ACTION:
10143     {
10144       return;
10145     }
10146
10147     // ---------- level actions  ----------------------------------------------
10148
10149     case CA_RESTART_LEVEL:
10150     {
10151       game.restart_level = TRUE;
10152
10153       break;
10154     }
10155
10156     case CA_SHOW_ENVELOPE:
10157     {
10158       int element = getSpecialActionElement(action_arg_element,
10159                                             action_arg_number, EL_ENVELOPE_1);
10160
10161       if (IS_ENVELOPE(element))
10162         local_player->show_envelope = element;
10163
10164       break;
10165     }
10166
10167     case CA_SET_LEVEL_TIME:
10168     {
10169       if (level.time > 0)       // only modify limited time value
10170       {
10171         TimeLeft = action_arg_number_new;
10172
10173         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10174
10175         DisplayGameControlValues();
10176
10177         if (!TimeLeft && game.time_limit)
10178           for (i = 0; i < MAX_PLAYERS; i++)
10179             KillPlayer(&stored_player[i]);
10180       }
10181
10182       break;
10183     }
10184
10185     case CA_SET_LEVEL_SCORE:
10186     {
10187       game.score = action_arg_number_new;
10188
10189       game_panel_controls[GAME_PANEL_SCORE].value = game.score;
10190
10191       DisplayGameControlValues();
10192
10193       break;
10194     }
10195
10196     case CA_SET_LEVEL_GEMS:
10197     {
10198       game.gems_still_needed = action_arg_number_new;
10199
10200       game.snapshot.collected_item = TRUE;
10201
10202       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
10203
10204       DisplayGameControlValues();
10205
10206       break;
10207     }
10208
10209     case CA_SET_LEVEL_WIND:
10210     {
10211       game.wind_direction = action_arg_direction;
10212
10213       break;
10214     }
10215
10216     case CA_SET_LEVEL_RANDOM_SEED:
10217     {
10218       // ensure that setting a new random seed while playing is predictable
10219       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10220
10221       break;
10222     }
10223
10224     // ---------- player actions  ---------------------------------------------
10225
10226     case CA_MOVE_PLAYER:
10227     case CA_MOVE_PLAYER_NEW:
10228     {
10229       // automatically move to the next field in specified direction
10230       for (i = 0; i < MAX_PLAYERS; i++)
10231         if (trigger_player_bits & (1 << i))
10232           if (action_type == CA_MOVE_PLAYER ||
10233               stored_player[i].MovPos == 0)
10234             stored_player[i].programmed_action = action_arg_direction;
10235
10236       break;
10237     }
10238
10239     case CA_EXIT_PLAYER:
10240     {
10241       for (i = 0; i < MAX_PLAYERS; i++)
10242         if (action_arg_player_bits & (1 << i))
10243           ExitPlayer(&stored_player[i]);
10244
10245       if (game.players_still_needed == 0)
10246         LevelSolved();
10247
10248       break;
10249     }
10250
10251     case CA_KILL_PLAYER:
10252     {
10253       for (i = 0; i < MAX_PLAYERS; i++)
10254         if (action_arg_player_bits & (1 << i))
10255           KillPlayer(&stored_player[i]);
10256
10257       break;
10258     }
10259
10260     case CA_SET_PLAYER_KEYS:
10261     {
10262       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10263       int element = getSpecialActionElement(action_arg_element,
10264                                             action_arg_number, EL_KEY_1);
10265
10266       if (IS_KEY(element))
10267       {
10268         for (i = 0; i < MAX_PLAYERS; i++)
10269         {
10270           if (trigger_player_bits & (1 << i))
10271           {
10272             stored_player[i].key[KEY_NR(element)] = key_state;
10273
10274             DrawGameDoorValues();
10275           }
10276         }
10277       }
10278
10279       break;
10280     }
10281
10282     case CA_SET_PLAYER_SPEED:
10283     {
10284       for (i = 0; i < MAX_PLAYERS; i++)
10285       {
10286         if (trigger_player_bits & (1 << i))
10287         {
10288           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10289
10290           if (action_arg == CA_ARG_SPEED_FASTER &&
10291               stored_player[i].cannot_move)
10292           {
10293             action_arg_number = STEPSIZE_VERY_SLOW;
10294           }
10295           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10296                    action_arg == CA_ARG_SPEED_FASTER)
10297           {
10298             action_arg_number = 2;
10299             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10300                            CA_MODE_MULTIPLY);
10301           }
10302           else if (action_arg == CA_ARG_NUMBER_RESET)
10303           {
10304             action_arg_number = level.initial_player_stepsize[i];
10305           }
10306
10307           move_stepsize =
10308             getModifiedActionNumber(move_stepsize,
10309                                     action_mode,
10310                                     action_arg_number,
10311                                     action_arg_number_min,
10312                                     action_arg_number_max);
10313
10314           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10315         }
10316       }
10317
10318       break;
10319     }
10320
10321     case CA_SET_PLAYER_SHIELD:
10322     {
10323       for (i = 0; i < MAX_PLAYERS; i++)
10324       {
10325         if (trigger_player_bits & (1 << i))
10326         {
10327           if (action_arg == CA_ARG_SHIELD_OFF)
10328           {
10329             stored_player[i].shield_normal_time_left = 0;
10330             stored_player[i].shield_deadly_time_left = 0;
10331           }
10332           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10333           {
10334             stored_player[i].shield_normal_time_left = 999999;
10335           }
10336           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10337           {
10338             stored_player[i].shield_normal_time_left = 999999;
10339             stored_player[i].shield_deadly_time_left = 999999;
10340           }
10341         }
10342       }
10343
10344       break;
10345     }
10346
10347     case CA_SET_PLAYER_GRAVITY:
10348     {
10349       for (i = 0; i < MAX_PLAYERS; i++)
10350       {
10351         if (trigger_player_bits & (1 << i))
10352         {
10353           stored_player[i].gravity =
10354             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10355              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10356              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10357              stored_player[i].gravity);
10358         }
10359       }
10360
10361       break;
10362     }
10363
10364     case CA_SET_PLAYER_ARTWORK:
10365     {
10366       for (i = 0; i < MAX_PLAYERS; i++)
10367       {
10368         if (trigger_player_bits & (1 << i))
10369         {
10370           int artwork_element = action_arg_element;
10371
10372           if (action_arg == CA_ARG_ELEMENT_RESET)
10373             artwork_element =
10374               (level.use_artwork_element[i] ? level.artwork_element[i] :
10375                stored_player[i].element_nr);
10376
10377           if (stored_player[i].artwork_element != artwork_element)
10378             stored_player[i].Frame = 0;
10379
10380           stored_player[i].artwork_element = artwork_element;
10381
10382           SetPlayerWaiting(&stored_player[i], FALSE);
10383
10384           // set number of special actions for bored and sleeping animation
10385           stored_player[i].num_special_action_bored =
10386             get_num_special_action(artwork_element,
10387                                    ACTION_BORING_1, ACTION_BORING_LAST);
10388           stored_player[i].num_special_action_sleeping =
10389             get_num_special_action(artwork_element,
10390                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10391         }
10392       }
10393
10394       break;
10395     }
10396
10397     case CA_SET_PLAYER_INVENTORY:
10398     {
10399       for (i = 0; i < MAX_PLAYERS; i++)
10400       {
10401         struct PlayerInfo *player = &stored_player[i];
10402         int j, k;
10403
10404         if (trigger_player_bits & (1 << i))
10405         {
10406           int inventory_element = action_arg_element;
10407
10408           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10409               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10410               action_arg == CA_ARG_ELEMENT_ACTION)
10411           {
10412             int element = inventory_element;
10413             int collect_count = element_info[element].collect_count_initial;
10414
10415             if (!IS_CUSTOM_ELEMENT(element))
10416               collect_count = 1;
10417
10418             if (collect_count == 0)
10419               player->inventory_infinite_element = element;
10420             else
10421               for (k = 0; k < collect_count; k++)
10422                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10423                   player->inventory_element[player->inventory_size++] =
10424                     element;
10425           }
10426           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10427                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10428                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10429           {
10430             if (player->inventory_infinite_element != EL_UNDEFINED &&
10431                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10432                                      action_arg_element_raw))
10433               player->inventory_infinite_element = EL_UNDEFINED;
10434
10435             for (k = 0, j = 0; j < player->inventory_size; j++)
10436             {
10437               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10438                                         action_arg_element_raw))
10439                 player->inventory_element[k++] = player->inventory_element[j];
10440             }
10441
10442             player->inventory_size = k;
10443           }
10444           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10445           {
10446             if (player->inventory_size > 0)
10447             {
10448               for (j = 0; j < player->inventory_size - 1; j++)
10449                 player->inventory_element[j] = player->inventory_element[j + 1];
10450
10451               player->inventory_size--;
10452             }
10453           }
10454           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10455           {
10456             if (player->inventory_size > 0)
10457               player->inventory_size--;
10458           }
10459           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10460           {
10461             player->inventory_infinite_element = EL_UNDEFINED;
10462             player->inventory_size = 0;
10463           }
10464           else if (action_arg == CA_ARG_INVENTORY_RESET)
10465           {
10466             player->inventory_infinite_element = EL_UNDEFINED;
10467             player->inventory_size = 0;
10468
10469             if (level.use_initial_inventory[i])
10470             {
10471               for (j = 0; j < level.initial_inventory_size[i]; j++)
10472               {
10473                 int element = level.initial_inventory_content[i][j];
10474                 int collect_count = element_info[element].collect_count_initial;
10475
10476                 if (!IS_CUSTOM_ELEMENT(element))
10477                   collect_count = 1;
10478
10479                 if (collect_count == 0)
10480                   player->inventory_infinite_element = element;
10481                 else
10482                   for (k = 0; k < collect_count; k++)
10483                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10484                       player->inventory_element[player->inventory_size++] =
10485                         element;
10486               }
10487             }
10488           }
10489         }
10490       }
10491
10492       break;
10493     }
10494
10495     // ---------- CE actions  -------------------------------------------------
10496
10497     case CA_SET_CE_VALUE:
10498     {
10499       int last_ce_value = CustomValue[x][y];
10500
10501       CustomValue[x][y] = action_arg_number_new;
10502
10503       if (CustomValue[x][y] != last_ce_value)
10504       {
10505         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10506         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10507
10508         if (CustomValue[x][y] == 0)
10509         {
10510           // reset change counter (else CE_VALUE_GETS_ZERO would not work)
10511           ChangeCount[x][y] = 0;        // allow at least one more change
10512
10513           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10514           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10515         }
10516       }
10517
10518       break;
10519     }
10520
10521     case CA_SET_CE_SCORE:
10522     {
10523       int last_ce_score = ei->collect_score;
10524
10525       ei->collect_score = action_arg_number_new;
10526
10527       if (ei->collect_score != last_ce_score)
10528       {
10529         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10530         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10531
10532         if (ei->collect_score == 0)
10533         {
10534           int xx, yy;
10535
10536           // reset change counter (else CE_SCORE_GETS_ZERO would not work)
10537           ChangeCount[x][y] = 0;        // allow at least one more change
10538
10539           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10540           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10541
10542           /*
10543             This is a very special case that seems to be a mixture between
10544             CheckElementChange() and CheckTriggeredElementChange(): while
10545             the first one only affects single elements that are triggered
10546             directly, the second one affects multiple elements in the playfield
10547             that are triggered indirectly by another element. This is a third
10548             case: Changing the CE score always affects multiple identical CEs,
10549             so every affected CE must be checked, not only the single CE for
10550             which the CE score was changed in the first place (as every instance
10551             of that CE shares the same CE score, and therefore also can change)!
10552           */
10553           SCAN_PLAYFIELD(xx, yy)
10554           {
10555             if (Tile[xx][yy] == element)
10556               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10557                                  CE_SCORE_GETS_ZERO);
10558           }
10559         }
10560       }
10561
10562       break;
10563     }
10564
10565     case CA_SET_CE_ARTWORK:
10566     {
10567       int artwork_element = action_arg_element;
10568       boolean reset_frame = FALSE;
10569       int xx, yy;
10570
10571       if (action_arg == CA_ARG_ELEMENT_RESET)
10572         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10573                            element);
10574
10575       if (ei->gfx_element != artwork_element)
10576         reset_frame = TRUE;
10577
10578       ei->gfx_element = artwork_element;
10579
10580       SCAN_PLAYFIELD(xx, yy)
10581       {
10582         if (Tile[xx][yy] == element)
10583         {
10584           if (reset_frame)
10585           {
10586             ResetGfxAnimation(xx, yy);
10587             ResetRandomAnimationValue(xx, yy);
10588           }
10589
10590           TEST_DrawLevelField(xx, yy);
10591         }
10592       }
10593
10594       break;
10595     }
10596
10597     // ---------- engine actions  ---------------------------------------------
10598
10599     case CA_SET_ENGINE_SCAN_MODE:
10600     {
10601       InitPlayfieldScanMode(action_arg);
10602
10603       break;
10604     }
10605
10606     default:
10607       break;
10608   }
10609 }
10610
10611 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10612 {
10613   int old_element = Tile[x][y];
10614   int new_element = GetElementFromGroupElement(element);
10615   int previous_move_direction = MovDir[x][y];
10616   int last_ce_value = CustomValue[x][y];
10617   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10618   boolean new_element_is_player = IS_PLAYER_ELEMENT(new_element);
10619   boolean add_player_onto_element = (new_element_is_player &&
10620                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10621                                      IS_WALKABLE(old_element));
10622
10623   if (!add_player_onto_element)
10624   {
10625     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10626       RemoveMovingField(x, y);
10627     else
10628       RemoveField(x, y);
10629
10630     Tile[x][y] = new_element;
10631
10632     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10633       MovDir[x][y] = previous_move_direction;
10634
10635     if (element_info[new_element].use_last_ce_value)
10636       CustomValue[x][y] = last_ce_value;
10637
10638     InitField_WithBug1(x, y, FALSE);
10639
10640     new_element = Tile[x][y];   // element may have changed
10641
10642     ResetGfxAnimation(x, y);
10643     ResetRandomAnimationValue(x, y);
10644
10645     TEST_DrawLevelField(x, y);
10646
10647     if (GFX_CRUMBLED(new_element))
10648       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10649
10650     if (old_element == EL_EXPLOSION)
10651     {
10652       Store[x][y] = Store2[x][y] = 0;
10653
10654       // check if new element replaces an exploding player, requiring cleanup
10655       if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
10656         StorePlayer[x][y] = 0;
10657     }
10658
10659     // check if element under the player changes from accessible to unaccessible
10660     // (needed for special case of dropping element which then changes)
10661     // (must be checked after creating new element for walkable group elements)
10662     if (IS_PLAYER(x, y) && !player_explosion_protected &&
10663         IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10664     {
10665       KillPlayer(PLAYERINFO(x, y));
10666
10667       return;
10668     }
10669   }
10670
10671   // "ChangeCount" not set yet to allow "entered by player" change one time
10672   if (new_element_is_player)
10673     RelocatePlayer(x, y, new_element);
10674
10675   if (is_change)
10676     ChangeCount[x][y]++;        // count number of changes in the same frame
10677
10678   TestIfBadThingTouchesPlayer(x, y);
10679   TestIfPlayerTouchesCustomElement(x, y);
10680   TestIfElementTouchesCustomElement(x, y);
10681 }
10682
10683 static void CreateField(int x, int y, int element)
10684 {
10685   CreateFieldExt(x, y, element, FALSE);
10686 }
10687
10688 static void CreateElementFromChange(int x, int y, int element)
10689 {
10690   element = GET_VALID_RUNTIME_ELEMENT(element);
10691
10692   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10693   {
10694     int old_element = Tile[x][y];
10695
10696     // prevent changed element from moving in same engine frame
10697     // unless both old and new element can either fall or move
10698     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10699         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10700       Stop[x][y] = TRUE;
10701   }
10702
10703   CreateFieldExt(x, y, element, TRUE);
10704 }
10705
10706 static boolean ChangeElement(int x, int y, int element, int page)
10707 {
10708   struct ElementInfo *ei = &element_info[element];
10709   struct ElementChangeInfo *change = &ei->change_page[page];
10710   int ce_value = CustomValue[x][y];
10711   int ce_score = ei->collect_score;
10712   int target_element;
10713   int old_element = Tile[x][y];
10714
10715   // always use default change event to prevent running into a loop
10716   if (ChangeEvent[x][y] == -1)
10717     ChangeEvent[x][y] = CE_DELAY;
10718
10719   if (ChangeEvent[x][y] == CE_DELAY)
10720   {
10721     // reset actual trigger element, trigger player and action element
10722     change->actual_trigger_element = EL_EMPTY;
10723     change->actual_trigger_player = EL_EMPTY;
10724     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10725     change->actual_trigger_side = CH_SIDE_NONE;
10726     change->actual_trigger_ce_value = 0;
10727     change->actual_trigger_ce_score = 0;
10728   }
10729
10730   // do not change elements more than a specified maximum number of changes
10731   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10732     return FALSE;
10733
10734   ChangeCount[x][y]++;          // count number of changes in the same frame
10735
10736   if (ei->has_anim_event)
10737     HandleGlobalAnimEventByElementChange(element, page, x, y);
10738
10739   if (change->explode)
10740   {
10741     Bang(x, y);
10742
10743     return TRUE;
10744   }
10745
10746   if (change->use_target_content)
10747   {
10748     boolean complete_replace = TRUE;
10749     boolean can_replace[3][3];
10750     int xx, yy;
10751
10752     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10753     {
10754       boolean is_empty;
10755       boolean is_walkable;
10756       boolean is_diggable;
10757       boolean is_collectible;
10758       boolean is_removable;
10759       boolean is_destructible;
10760       int ex = x + xx - 1;
10761       int ey = y + yy - 1;
10762       int content_element = change->target_content.e[xx][yy];
10763       int e;
10764
10765       can_replace[xx][yy] = TRUE;
10766
10767       if (ex == x && ey == y)   // do not check changing element itself
10768         continue;
10769
10770       if (content_element == EL_EMPTY_SPACE)
10771       {
10772         can_replace[xx][yy] = FALSE;    // do not replace border with space
10773
10774         continue;
10775       }
10776
10777       if (!IN_LEV_FIELD(ex, ey))
10778       {
10779         can_replace[xx][yy] = FALSE;
10780         complete_replace = FALSE;
10781
10782         continue;
10783       }
10784
10785       e = Tile[ex][ey];
10786
10787       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10788         e = MovingOrBlocked2Element(ex, ey);
10789
10790       is_empty = (IS_FREE(ex, ey) ||
10791                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10792
10793       is_walkable     = (is_empty || IS_WALKABLE(e));
10794       is_diggable     = (is_empty || IS_DIGGABLE(e));
10795       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10796       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10797       is_removable    = (is_diggable || is_collectible);
10798
10799       can_replace[xx][yy] =
10800         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10801           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10802           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10803           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10804           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10805           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10806          !(IS_PLAYER(ex, ey) && IS_PLAYER_ELEMENT(content_element)));
10807
10808       if (!can_replace[xx][yy])
10809         complete_replace = FALSE;
10810     }
10811
10812     if (!change->only_if_complete || complete_replace)
10813     {
10814       boolean something_has_changed = FALSE;
10815
10816       if (change->only_if_complete && change->use_random_replace &&
10817           RND(100) < change->random_percentage)
10818         return FALSE;
10819
10820       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10821       {
10822         int ex = x + xx - 1;
10823         int ey = y + yy - 1;
10824         int content_element;
10825
10826         if (can_replace[xx][yy] && (!change->use_random_replace ||
10827                                     RND(100) < change->random_percentage))
10828         {
10829           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10830             RemoveMovingField(ex, ey);
10831
10832           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10833
10834           content_element = change->target_content.e[xx][yy];
10835           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10836                                               ce_value, ce_score);
10837
10838           CreateElementFromChange(ex, ey, target_element);
10839
10840           something_has_changed = TRUE;
10841
10842           // for symmetry reasons, freeze newly created border elements
10843           if (ex != x || ey != y)
10844             Stop[ex][ey] = TRUE;        // no more moving in this frame
10845         }
10846       }
10847
10848       if (something_has_changed)
10849       {
10850         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10851         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10852       }
10853     }
10854   }
10855   else
10856   {
10857     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10858                                         ce_value, ce_score);
10859
10860     if (element == EL_DIAGONAL_GROWING ||
10861         element == EL_DIAGONAL_SHRINKING)
10862     {
10863       target_element = Store[x][y];
10864
10865       Store[x][y] = EL_EMPTY;
10866     }
10867
10868     // special case: element changes to player (and may be kept if walkable)
10869     if (IS_PLAYER_ELEMENT(target_element) && !level.keep_walkable_ce)
10870       CreateElementFromChange(x, y, EL_EMPTY);
10871
10872     CreateElementFromChange(x, y, target_element);
10873
10874     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10875     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10876   }
10877
10878   // this uses direct change before indirect change
10879   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10880
10881   return TRUE;
10882 }
10883
10884 static void HandleElementChange(int x, int y, int page)
10885 {
10886   int element = MovingOrBlocked2Element(x, y);
10887   struct ElementInfo *ei = &element_info[element];
10888   struct ElementChangeInfo *change = &ei->change_page[page];
10889   boolean handle_action_before_change = FALSE;
10890
10891 #ifdef DEBUG
10892   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10893       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10894   {
10895     Debug("game:playing:HandleElementChange", "%d,%d: element = %d ('%s')",
10896           x, y, element, element_info[element].token_name);
10897     Debug("game:playing:HandleElementChange", "This should never happen!");
10898   }
10899 #endif
10900
10901   // this can happen with classic bombs on walkable, changing elements
10902   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10903   {
10904     return;
10905   }
10906
10907   if (ChangeDelay[x][y] == 0)           // initialize element change
10908   {
10909     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10910
10911     if (change->can_change)
10912     {
10913       // !!! not clear why graphic animation should be reset at all here !!!
10914       // !!! UPDATE: but is needed for correct Snake Bite tail animation !!!
10915       // !!! SOLUTION: do not reset if graphics engine set to 4 or above !!!
10916
10917       /*
10918         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10919
10920         When using an animation frame delay of 1 (this only happens with
10921         "sp_zonk.moving.left/right" in the classic graphics), the default
10922         (non-moving) animation shows wrong animation frames (while the
10923         moving animation, like "sp_zonk.moving.left/right", is correct,
10924         so this graphical bug never shows up with the classic graphics).
10925         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10926         be drawn instead of the correct frames 0,1,2,3. This is caused by
10927         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10928         an element change: First when the change delay ("ChangeDelay[][]")
10929         counter has reached zero after decrementing, then a second time in
10930         the next frame (after "GfxFrame[][]" was already incremented) when
10931         "ChangeDelay[][]" is reset to the initial delay value again.
10932
10933         This causes frame 0 to be drawn twice, while the last frame won't
10934         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10935
10936         As some animations may already be cleverly designed around this bug
10937         (at least the "Snake Bite" snake tail animation does this), it cannot
10938         simply be fixed here without breaking such existing animations.
10939         Unfortunately, it cannot easily be detected if a graphics set was
10940         designed "before" or "after" the bug was fixed. As a workaround,
10941         a new graphics set option "game.graphics_engine_version" was added
10942         to be able to specify the game's major release version for which the
10943         graphics set was designed, which can then be used to decide if the
10944         bugfix should be used (version 4 and above) or not (version 3 or
10945         below, or if no version was specified at all, as with old sets).
10946
10947         (The wrong/fixed animation frames can be tested with the test level set
10948         "test_gfxframe" and level "000", which contains a specially prepared
10949         custom element at level position (x/y) == (11/9) which uses the zonk
10950         animation mentioned above. Using "game.graphics_engine_version: 4"
10951         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10952         This can also be seen from the debug output for this test element.)
10953       */
10954
10955       // when a custom element is about to change (for example by change delay),
10956       // do not reset graphic animation when the custom element is moving
10957       if (game.graphics_engine_version < 4 &&
10958           !IS_MOVING(x, y))
10959       {
10960         ResetGfxAnimation(x, y);
10961         ResetRandomAnimationValue(x, y);
10962       }
10963
10964       if (change->pre_change_function)
10965         change->pre_change_function(x, y);
10966     }
10967   }
10968
10969   ChangeDelay[x][y]--;
10970
10971   if (ChangeDelay[x][y] != 0)           // continue element change
10972   {
10973     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10974
10975     // also needed if CE can not change, but has CE delay with CE action
10976     if (IS_ANIMATED(graphic))
10977       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10978
10979     if (change->can_change)
10980     {
10981       if (change->change_function)
10982         change->change_function(x, y);
10983     }
10984   }
10985   else                                  // finish element change
10986   {
10987     if (ChangePage[x][y] != -1)         // remember page from delayed change
10988     {
10989       page = ChangePage[x][y];
10990       ChangePage[x][y] = -1;
10991
10992       change = &ei->change_page[page];
10993     }
10994
10995     if (IS_MOVING(x, y))                // never change a running system ;-)
10996     {
10997       ChangeDelay[x][y] = 1;            // try change after next move step
10998       ChangePage[x][y] = page;          // remember page to use for change
10999
11000       return;
11001     }
11002
11003     // special case: set new level random seed before changing element
11004     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
11005       handle_action_before_change = TRUE;
11006
11007     if (change->has_action && handle_action_before_change)
11008       ExecuteCustomElementAction(x, y, element, page);
11009
11010     if (change->can_change)
11011     {
11012       if (ChangeElement(x, y, element, page))
11013       {
11014         if (change->post_change_function)
11015           change->post_change_function(x, y);
11016       }
11017     }
11018
11019     if (change->has_action && !handle_action_before_change)
11020       ExecuteCustomElementAction(x, y, element, page);
11021   }
11022 }
11023
11024 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11025                                               int trigger_element,
11026                                               int trigger_event,
11027                                               int trigger_player,
11028                                               int trigger_side,
11029                                               int trigger_page)
11030 {
11031   boolean change_done_any = FALSE;
11032   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11033   int i;
11034
11035   if (!(trigger_events[trigger_element][trigger_event]))
11036     return FALSE;
11037
11038   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11039
11040   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11041   {
11042     int element = EL_CUSTOM_START + i;
11043     boolean change_done = FALSE;
11044     int p;
11045
11046     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11047         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11048       continue;
11049
11050     for (p = 0; p < element_info[element].num_change_pages; p++)
11051     {
11052       struct ElementChangeInfo *change = &element_info[element].change_page[p];
11053
11054       if (change->can_change_or_has_action &&
11055           change->has_event[trigger_event] &&
11056           change->trigger_side & trigger_side &&
11057           change->trigger_player & trigger_player &&
11058           change->trigger_page & trigger_page_bits &&
11059           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11060       {
11061         change->actual_trigger_element = trigger_element;
11062         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11063         change->actual_trigger_player_bits = trigger_player;
11064         change->actual_trigger_side = trigger_side;
11065         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11066         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11067
11068         if ((change->can_change && !change_done) || change->has_action)
11069         {
11070           int x, y;
11071
11072           SCAN_PLAYFIELD(x, y)
11073           {
11074             if (Tile[x][y] == element)
11075             {
11076               if (change->can_change && !change_done)
11077               {
11078                 // if element already changed in this frame, not only prevent
11079                 // another element change (checked in ChangeElement()), but
11080                 // also prevent additional element actions for this element
11081
11082                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11083                     !level.use_action_after_change_bug)
11084                   continue;
11085
11086                 ChangeDelay[x][y] = 1;
11087                 ChangeEvent[x][y] = trigger_event;
11088
11089                 HandleElementChange(x, y, p);
11090               }
11091               else if (change->has_action)
11092               {
11093                 // if element already changed in this frame, not only prevent
11094                 // another element change (checked in ChangeElement()), but
11095                 // also prevent additional element actions for this element
11096
11097                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11098                     !level.use_action_after_change_bug)
11099                   continue;
11100
11101                 ExecuteCustomElementAction(x, y, element, p);
11102                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11103               }
11104             }
11105           }
11106
11107           if (change->can_change)
11108           {
11109             change_done = TRUE;
11110             change_done_any = TRUE;
11111           }
11112         }
11113       }
11114     }
11115   }
11116
11117   RECURSION_LOOP_DETECTION_END();
11118
11119   return change_done_any;
11120 }
11121
11122 static boolean CheckElementChangeExt(int x, int y,
11123                                      int element,
11124                                      int trigger_element,
11125                                      int trigger_event,
11126                                      int trigger_player,
11127                                      int trigger_side)
11128 {
11129   boolean change_done = FALSE;
11130   int p;
11131
11132   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11133       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11134     return FALSE;
11135
11136   if (Tile[x][y] == EL_BLOCKED)
11137   {
11138     Blocked2Moving(x, y, &x, &y);
11139     element = Tile[x][y];
11140   }
11141
11142   // check if element has already changed or is about to change after moving
11143   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11144        Tile[x][y] != element) ||
11145
11146       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11147        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11148         ChangePage[x][y] != -1)))
11149     return FALSE;
11150
11151   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11152
11153   for (p = 0; p < element_info[element].num_change_pages; p++)
11154   {
11155     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11156
11157     /* check trigger element for all events where the element that is checked
11158        for changing interacts with a directly adjacent element -- this is
11159        different to element changes that affect other elements to change on the
11160        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11161     boolean check_trigger_element =
11162       (trigger_event == CE_NEXT_TO_X ||
11163        trigger_event == CE_TOUCHING_X ||
11164        trigger_event == CE_HITTING_X ||
11165        trigger_event == CE_HIT_BY_X ||
11166        trigger_event == CE_DIGGING_X); // this one was forgotten until 3.2.3
11167
11168     if (change->can_change_or_has_action &&
11169         change->has_event[trigger_event] &&
11170         change->trigger_side & trigger_side &&
11171         change->trigger_player & trigger_player &&
11172         (!check_trigger_element ||
11173          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11174     {
11175       change->actual_trigger_element = trigger_element;
11176       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11177       change->actual_trigger_player_bits = trigger_player;
11178       change->actual_trigger_side = trigger_side;
11179       change->actual_trigger_ce_value = CustomValue[x][y];
11180       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11181
11182       // special case: trigger element not at (x,y) position for some events
11183       if (check_trigger_element)
11184       {
11185         static struct
11186         {
11187           int dx, dy;
11188         } move_xy[] =
11189           {
11190             {  0,  0 },
11191             { -1,  0 },
11192             { +1,  0 },
11193             {  0,  0 },
11194             {  0, -1 },
11195             {  0,  0 }, { 0, 0 }, { 0, 0 },
11196             {  0, +1 }
11197           };
11198
11199         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11200         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11201
11202         change->actual_trigger_ce_value = CustomValue[xx][yy];
11203         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11204       }
11205
11206       if (change->can_change && !change_done)
11207       {
11208         ChangeDelay[x][y] = 1;
11209         ChangeEvent[x][y] = trigger_event;
11210
11211         HandleElementChange(x, y, p);
11212
11213         change_done = TRUE;
11214       }
11215       else if (change->has_action)
11216       {
11217         ExecuteCustomElementAction(x, y, element, p);
11218         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11219       }
11220     }
11221   }
11222
11223   RECURSION_LOOP_DETECTION_END();
11224
11225   return change_done;
11226 }
11227
11228 static void PlayPlayerSound(struct PlayerInfo *player)
11229 {
11230   int jx = player->jx, jy = player->jy;
11231   int sound_element = player->artwork_element;
11232   int last_action = player->last_action_waiting;
11233   int action = player->action_waiting;
11234
11235   if (player->is_waiting)
11236   {
11237     if (action != last_action)
11238       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11239     else
11240       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11241   }
11242   else
11243   {
11244     if (action != last_action)
11245       StopSound(element_info[sound_element].sound[last_action]);
11246
11247     if (last_action == ACTION_SLEEPING)
11248       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11249   }
11250 }
11251
11252 static void PlayAllPlayersSound(void)
11253 {
11254   int i;
11255
11256   for (i = 0; i < MAX_PLAYERS; i++)
11257     if (stored_player[i].active)
11258       PlayPlayerSound(&stored_player[i]);
11259 }
11260
11261 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11262 {
11263   boolean last_waiting = player->is_waiting;
11264   int move_dir = player->MovDir;
11265
11266   player->dir_waiting = move_dir;
11267   player->last_action_waiting = player->action_waiting;
11268
11269   if (is_waiting)
11270   {
11271     if (!last_waiting)          // not waiting -> waiting
11272     {
11273       player->is_waiting = TRUE;
11274
11275       player->frame_counter_bored =
11276         FrameCounter +
11277         game.player_boring_delay_fixed +
11278         GetSimpleRandom(game.player_boring_delay_random);
11279       player->frame_counter_sleeping =
11280         FrameCounter +
11281         game.player_sleeping_delay_fixed +
11282         GetSimpleRandom(game.player_sleeping_delay_random);
11283
11284       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11285     }
11286
11287     if (game.player_sleeping_delay_fixed +
11288         game.player_sleeping_delay_random > 0 &&
11289         player->anim_delay_counter == 0 &&
11290         player->post_delay_counter == 0 &&
11291         FrameCounter >= player->frame_counter_sleeping)
11292       player->is_sleeping = TRUE;
11293     else if (game.player_boring_delay_fixed +
11294              game.player_boring_delay_random > 0 &&
11295              FrameCounter >= player->frame_counter_bored)
11296       player->is_bored = TRUE;
11297
11298     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11299                               player->is_bored ? ACTION_BORING :
11300                               ACTION_WAITING);
11301
11302     if (player->is_sleeping && player->use_murphy)
11303     {
11304       // special case for sleeping Murphy when leaning against non-free tile
11305
11306       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11307           (Tile[player->jx - 1][player->jy] != EL_EMPTY &&
11308            !IS_MOVING(player->jx - 1, player->jy)))
11309         move_dir = MV_LEFT;
11310       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11311                (Tile[player->jx + 1][player->jy] != EL_EMPTY &&
11312                 !IS_MOVING(player->jx + 1, player->jy)))
11313         move_dir = MV_RIGHT;
11314       else
11315         player->is_sleeping = FALSE;
11316
11317       player->dir_waiting = move_dir;
11318     }
11319
11320     if (player->is_sleeping)
11321     {
11322       if (player->num_special_action_sleeping > 0)
11323       {
11324         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11325         {
11326           int last_special_action = player->special_action_sleeping;
11327           int num_special_action = player->num_special_action_sleeping;
11328           int special_action =
11329             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11330              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11331              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11332              last_special_action + 1 : ACTION_SLEEPING);
11333           int special_graphic =
11334             el_act_dir2img(player->artwork_element, special_action, move_dir);
11335
11336           player->anim_delay_counter =
11337             graphic_info[special_graphic].anim_delay_fixed +
11338             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11339           player->post_delay_counter =
11340             graphic_info[special_graphic].post_delay_fixed +
11341             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11342
11343           player->special_action_sleeping = special_action;
11344         }
11345
11346         if (player->anim_delay_counter > 0)
11347         {
11348           player->action_waiting = player->special_action_sleeping;
11349           player->anim_delay_counter--;
11350         }
11351         else if (player->post_delay_counter > 0)
11352         {
11353           player->post_delay_counter--;
11354         }
11355       }
11356     }
11357     else if (player->is_bored)
11358     {
11359       if (player->num_special_action_bored > 0)
11360       {
11361         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11362         {
11363           int special_action =
11364             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11365           int special_graphic =
11366             el_act_dir2img(player->artwork_element, special_action, move_dir);
11367
11368           player->anim_delay_counter =
11369             graphic_info[special_graphic].anim_delay_fixed +
11370             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11371           player->post_delay_counter =
11372             graphic_info[special_graphic].post_delay_fixed +
11373             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11374
11375           player->special_action_bored = special_action;
11376         }
11377
11378         if (player->anim_delay_counter > 0)
11379         {
11380           player->action_waiting = player->special_action_bored;
11381           player->anim_delay_counter--;
11382         }
11383         else if (player->post_delay_counter > 0)
11384         {
11385           player->post_delay_counter--;
11386         }
11387       }
11388     }
11389   }
11390   else if (last_waiting)        // waiting -> not waiting
11391   {
11392     player->is_waiting = FALSE;
11393     player->is_bored = FALSE;
11394     player->is_sleeping = FALSE;
11395
11396     player->frame_counter_bored = -1;
11397     player->frame_counter_sleeping = -1;
11398
11399     player->anim_delay_counter = 0;
11400     player->post_delay_counter = 0;
11401
11402     player->dir_waiting = player->MovDir;
11403     player->action_waiting = ACTION_DEFAULT;
11404
11405     player->special_action_bored = ACTION_DEFAULT;
11406     player->special_action_sleeping = ACTION_DEFAULT;
11407   }
11408 }
11409
11410 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
11411 {
11412   if ((!player->is_moving  && player->was_moving) ||
11413       (player->MovPos == 0 && player->was_moving) ||
11414       (player->is_snapping && !player->was_snapping) ||
11415       (player->is_dropping && !player->was_dropping))
11416   {
11417     if (!CheckSaveEngineSnapshotToList())
11418       return;
11419
11420     player->was_moving = FALSE;
11421     player->was_snapping = TRUE;
11422     player->was_dropping = TRUE;
11423   }
11424   else
11425   {
11426     if (player->is_moving)
11427       player->was_moving = TRUE;
11428
11429     if (!player->is_snapping)
11430       player->was_snapping = FALSE;
11431
11432     if (!player->is_dropping)
11433       player->was_dropping = FALSE;
11434   }
11435
11436   static struct MouseActionInfo mouse_action_last = { 0 };
11437   struct MouseActionInfo mouse_action = player->effective_mouse_action;
11438   boolean new_released = (!mouse_action.button && mouse_action_last.button);
11439
11440   if (new_released)
11441     CheckSaveEngineSnapshotToList();
11442
11443   mouse_action_last = mouse_action;
11444 }
11445
11446 static void CheckSingleStepMode(struct PlayerInfo *player)
11447 {
11448   if (tape.single_step && tape.recording && !tape.pausing)
11449   {
11450     // as it is called "single step mode", just return to pause mode when the
11451     // player stopped moving after one tile (or never starts moving at all)
11452     // (reverse logic needed here in case single step mode used in team mode)
11453     if (player->is_moving ||
11454         player->is_pushing ||
11455         player->is_dropping_pressed ||
11456         player->effective_mouse_action.button)
11457       game.enter_single_step_mode = FALSE;
11458   }
11459
11460   CheckSaveEngineSnapshot(player);
11461 }
11462
11463 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11464 {
11465   int left      = player_action & JOY_LEFT;
11466   int right     = player_action & JOY_RIGHT;
11467   int up        = player_action & JOY_UP;
11468   int down      = player_action & JOY_DOWN;
11469   int button1   = player_action & JOY_BUTTON_1;
11470   int button2   = player_action & JOY_BUTTON_2;
11471   int dx        = (left ? -1 : right ? 1 : 0);
11472   int dy        = (up   ? -1 : down  ? 1 : 0);
11473
11474   if (!player->active || tape.pausing)
11475     return 0;
11476
11477   if (player_action)
11478   {
11479     if (button1)
11480       SnapField(player, dx, dy);
11481     else
11482     {
11483       if (button2)
11484         DropElement(player);
11485
11486       MovePlayer(player, dx, dy);
11487     }
11488
11489     CheckSingleStepMode(player);
11490
11491     SetPlayerWaiting(player, FALSE);
11492
11493     return player_action;
11494   }
11495   else
11496   {
11497     // no actions for this player (no input at player's configured device)
11498
11499     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11500     SnapField(player, 0, 0);
11501     CheckGravityMovementWhenNotMoving(player);
11502
11503     if (player->MovPos == 0)
11504       SetPlayerWaiting(player, TRUE);
11505
11506     if (player->MovPos == 0)    // needed for tape.playing
11507       player->is_moving = FALSE;
11508
11509     player->is_dropping = FALSE;
11510     player->is_dropping_pressed = FALSE;
11511     player->drop_pressed_delay = 0;
11512
11513     CheckSingleStepMode(player);
11514
11515     return 0;
11516   }
11517 }
11518
11519 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11520                                          byte *tape_action)
11521 {
11522   if (!tape.use_mouse_actions)
11523     return;
11524
11525   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11526   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11527   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11528 }
11529
11530 static void SetTapeActionFromMouseAction(byte *tape_action,
11531                                          struct MouseActionInfo *mouse_action)
11532 {
11533   if (!tape.use_mouse_actions)
11534     return;
11535
11536   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11537   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11538   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11539 }
11540
11541 static void CheckLevelSolved(void)
11542 {
11543   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11544   {
11545     if (game_em.level_solved &&
11546         !game_em.game_over)                             // game won
11547     {
11548       LevelSolved();
11549
11550       game_em.game_over = TRUE;
11551
11552       game.all_players_gone = TRUE;
11553     }
11554
11555     if (game_em.game_over)                              // game lost
11556       game.all_players_gone = TRUE;
11557   }
11558   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11559   {
11560     if (game_sp.level_solved &&
11561         !game_sp.game_over)                             // game won
11562     {
11563       LevelSolved();
11564
11565       game_sp.game_over = TRUE;
11566
11567       game.all_players_gone = TRUE;
11568     }
11569
11570     if (game_sp.game_over)                              // game lost
11571       game.all_players_gone = TRUE;
11572   }
11573   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11574   {
11575     if (game_mm.level_solved &&
11576         !game_mm.game_over)                             // game won
11577     {
11578       LevelSolved();
11579
11580       game_mm.game_over = TRUE;
11581
11582       game.all_players_gone = TRUE;
11583     }
11584
11585     if (game_mm.game_over)                              // game lost
11586       game.all_players_gone = TRUE;
11587   }
11588 }
11589
11590 static void CheckLevelTime_StepCounter(void)
11591 {
11592   int i;
11593
11594   TimePlayed++;
11595
11596   if (TimeLeft > 0)
11597   {
11598     TimeLeft--;
11599
11600     if (TimeLeft <= 10 && game.time_limit && !game.LevelSolved)
11601       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11602
11603     game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11604
11605     DisplayGameControlValues();
11606
11607     if (!TimeLeft && game.time_limit && !game.LevelSolved)
11608       for (i = 0; i < MAX_PLAYERS; i++)
11609         KillPlayer(&stored_player[i]);
11610   }
11611   else if (game.no_level_time_limit && !game.all_players_gone)
11612   {
11613     game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11614
11615     DisplayGameControlValues();
11616   }
11617 }
11618
11619 static void CheckLevelTime(void)
11620 {
11621   int i;
11622
11623   if (TimeFrames >= FRAMES_PER_SECOND)
11624   {
11625     TimeFrames = 0;
11626     TapeTime++;
11627
11628     for (i = 0; i < MAX_PLAYERS; i++)
11629     {
11630       struct PlayerInfo *player = &stored_player[i];
11631
11632       if (SHIELD_ON(player))
11633       {
11634         player->shield_normal_time_left--;
11635
11636         if (player->shield_deadly_time_left > 0)
11637           player->shield_deadly_time_left--;
11638       }
11639     }
11640
11641     if (!game.LevelSolved && !level.use_step_counter)
11642     {
11643       TimePlayed++;
11644
11645       if (TimeLeft > 0)
11646       {
11647         TimeLeft--;
11648
11649         if (TimeLeft <= 10 && game.time_limit)
11650           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11651
11652         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11653            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11654
11655         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11656
11657         if (!TimeLeft && game.time_limit)
11658         {
11659           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11660             game_em.lev->killed_out_of_time = TRUE;
11661           else
11662             for (i = 0; i < MAX_PLAYERS; i++)
11663               KillPlayer(&stored_player[i]);
11664         }
11665       }
11666       else if (game.no_level_time_limit && !game.all_players_gone)
11667       {
11668         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11669       }
11670
11671       game_em.lev->time = (game.no_level_time_limit ? TimePlayed : TimeLeft);
11672     }
11673
11674     if (tape.recording || tape.playing)
11675       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11676   }
11677
11678   if (tape.recording || tape.playing)
11679     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11680
11681   UpdateAndDisplayGameControlValues();
11682 }
11683
11684 void AdvanceFrameAndPlayerCounters(int player_nr)
11685 {
11686   int i;
11687
11688   // advance frame counters (global frame counter and time frame counter)
11689   FrameCounter++;
11690   TimeFrames++;
11691
11692   // advance player counters (counters for move delay, move animation etc.)
11693   for (i = 0; i < MAX_PLAYERS; i++)
11694   {
11695     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11696     int move_delay_value = stored_player[i].move_delay_value;
11697     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11698
11699     if (!advance_player_counters)       // not all players may be affected
11700       continue;
11701
11702     if (move_frames == 0)       // less than one move per game frame
11703     {
11704       int stepsize = TILEX / move_delay_value;
11705       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11706       int count = (stored_player[i].is_moving ?
11707                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11708
11709       if (count % delay == 0)
11710         move_frames = 1;
11711     }
11712
11713     stored_player[i].Frame += move_frames;
11714
11715     if (stored_player[i].MovPos != 0)
11716       stored_player[i].StepFrame += move_frames;
11717
11718     if (stored_player[i].move_delay > 0)
11719       stored_player[i].move_delay--;
11720
11721     // due to bugs in previous versions, counter must count up, not down
11722     if (stored_player[i].push_delay != -1)
11723       stored_player[i].push_delay++;
11724
11725     if (stored_player[i].drop_delay > 0)
11726       stored_player[i].drop_delay--;
11727
11728     if (stored_player[i].is_dropping_pressed)
11729       stored_player[i].drop_pressed_delay++;
11730   }
11731 }
11732
11733 void AdvanceFrameCounter(void)
11734 {
11735   FrameCounter++;
11736 }
11737
11738 void AdvanceGfxFrame(void)
11739 {
11740   int x, y;
11741
11742   SCAN_PLAYFIELD(x, y)
11743   {
11744     GfxFrame[x][y]++;
11745   }
11746 }
11747
11748 static void HandleMouseAction(struct MouseActionInfo *mouse_action,
11749                               struct MouseActionInfo *mouse_action_last)
11750 {
11751   if (mouse_action->button)
11752   {
11753     int new_button = (mouse_action->button && mouse_action_last->button == 0);
11754     int ch_button = CH_SIDE_FROM_BUTTON(mouse_action->button);
11755     int x = mouse_action->lx;
11756     int y = mouse_action->ly;
11757     int element = Tile[x][y];
11758
11759     if (new_button)
11760     {
11761       CheckElementChangeByMouse(x, y, element, CE_CLICKED_BY_MOUSE, ch_button);
11762       CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_CLICKED_ON_X,
11763                                          ch_button);
11764     }
11765
11766     CheckElementChangeByMouse(x, y, element, CE_PRESSED_BY_MOUSE, ch_button);
11767     CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_PRESSED_ON_X,
11768                                        ch_button);
11769
11770     if (level.use_step_counter)
11771     {
11772       boolean counted_click = FALSE;
11773
11774       // element clicked that can change when clicked/pressed
11775       if (CAN_CHANGE_OR_HAS_ACTION(element) &&
11776           (HAS_ANY_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
11777            HAS_ANY_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE)))
11778         counted_click = TRUE;
11779
11780       // element clicked that can trigger change when clicked/pressed
11781       if (trigger_events[element][CE_MOUSE_CLICKED_ON_X] ||
11782           trigger_events[element][CE_MOUSE_PRESSED_ON_X])
11783         counted_click = TRUE;
11784
11785       if (new_button && counted_click)
11786         CheckLevelTime_StepCounter();
11787     }
11788   }
11789 }
11790
11791 void StartGameActions(boolean init_network_game, boolean record_tape,
11792                       int random_seed)
11793 {
11794   unsigned int new_random_seed = InitRND(random_seed);
11795
11796   if (record_tape)
11797     TapeStartRecording(new_random_seed);
11798
11799   if (setup.auto_pause_on_start && !tape.pausing)
11800     TapeTogglePause(TAPE_TOGGLE_MANUAL);
11801
11802   if (init_network_game)
11803   {
11804     SendToServer_LevelFile();
11805     SendToServer_StartPlaying();
11806
11807     return;
11808   }
11809
11810   InitGame();
11811 }
11812
11813 static void GameActionsExt(void)
11814 {
11815 #if 0
11816   static unsigned int game_frame_delay = 0;
11817 #endif
11818   unsigned int game_frame_delay_value;
11819   byte *recorded_player_action;
11820   byte summarized_player_action = 0;
11821   byte tape_action[MAX_TAPE_ACTIONS] = { 0 };
11822   int i;
11823
11824   // detect endless loops, caused by custom element programming
11825   if (recursion_loop_detected && recursion_loop_depth == 0)
11826   {
11827     char *message = getStringCat3("Internal Error! Element ",
11828                                   EL_NAME(recursion_loop_element),
11829                                   " caused endless loop! Quit the game?");
11830
11831     Warn("element '%s' caused endless loop in game engine",
11832          EL_NAME(recursion_loop_element));
11833
11834     RequestQuitGameExt(program.headless, level_editor_test_game, message);
11835
11836     recursion_loop_detected = FALSE;    // if game should be continued
11837
11838     free(message);
11839
11840     return;
11841   }
11842
11843   if (game.restart_level)
11844     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11845
11846   CheckLevelSolved();
11847
11848   if (game.LevelSolved && !game.LevelSolved_GameEnd)
11849     GameWon();
11850
11851   if (game.all_players_gone && !TAPE_IS_STOPPED(tape))
11852     TapeStop();
11853
11854   if (game_status != GAME_MODE_PLAYING)         // status might have changed
11855     return;
11856
11857   game_frame_delay_value =
11858     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11859
11860   if (tape.playing && tape.warp_forward && !tape.pausing)
11861     game_frame_delay_value = 0;
11862
11863   SetVideoFrameDelay(game_frame_delay_value);
11864
11865   // (de)activate virtual buttons depending on current game status
11866   if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
11867   {
11868     if (game.all_players_gone)  // if no players there to be controlled anymore
11869       SetOverlayActive(FALSE);
11870     else if (!tape.playing)     // if game continues after tape stopped playing
11871       SetOverlayActive(TRUE);
11872   }
11873
11874 #if 0
11875 #if 0
11876   // ---------- main game synchronization point ----------
11877
11878   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11879
11880   Debug("game:playing:skip", "skip == %d", skip);
11881
11882 #else
11883   // ---------- main game synchronization point ----------
11884
11885   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11886 #endif
11887 #endif
11888
11889   if (network_playing && !network_player_action_received)
11890   {
11891     // try to get network player actions in time
11892
11893     // last chance to get network player actions without main loop delay
11894     HandleNetworking();
11895
11896     // game was quit by network peer
11897     if (game_status != GAME_MODE_PLAYING)
11898       return;
11899
11900     // check if network player actions still missing and game still running
11901     if (!network_player_action_received && !checkGameEnded())
11902       return;           // failed to get network player actions in time
11903
11904     // do not yet reset "network_player_action_received" (for tape.pausing)
11905   }
11906
11907   if (tape.pausing)
11908     return;
11909
11910   // at this point we know that we really continue executing the game
11911
11912   network_player_action_received = FALSE;
11913
11914   // when playing tape, read previously recorded player input from tape data
11915   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11916
11917   local_player->effective_mouse_action = local_player->mouse_action;
11918
11919   if (recorded_player_action != NULL)
11920     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11921                                  recorded_player_action);
11922
11923   // TapePlayAction() may return NULL when toggling to "pause before death"
11924   if (tape.pausing)
11925     return;
11926
11927   if (tape.set_centered_player)
11928   {
11929     game.centered_player_nr_next = tape.centered_player_nr_next;
11930     game.set_centered_player = TRUE;
11931   }
11932
11933   for (i = 0; i < MAX_PLAYERS; i++)
11934   {
11935     summarized_player_action |= stored_player[i].action;
11936
11937     if (!network_playing && (game.team_mode || tape.playing))
11938       stored_player[i].effective_action = stored_player[i].action;
11939   }
11940
11941   if (network_playing && !checkGameEnded())
11942     SendToServer_MovePlayer(summarized_player_action);
11943
11944   // summarize all actions at local players mapped input device position
11945   // (this allows using different input devices in single player mode)
11946   if (!network.enabled && !game.team_mode)
11947     stored_player[map_player_action[local_player->index_nr]].effective_action =
11948       summarized_player_action;
11949
11950   // summarize all actions at centered player in local team mode
11951   if (tape.recording &&
11952       setup.team_mode && !network.enabled &&
11953       setup.input_on_focus &&
11954       game.centered_player_nr != -1)
11955   {
11956     for (i = 0; i < MAX_PLAYERS; i++)
11957       stored_player[map_player_action[i]].effective_action =
11958         (i == game.centered_player_nr ? summarized_player_action : 0);
11959   }
11960
11961   if (recorded_player_action != NULL)
11962     for (i = 0; i < MAX_PLAYERS; i++)
11963       stored_player[i].effective_action = recorded_player_action[i];
11964
11965   for (i = 0; i < MAX_PLAYERS; i++)
11966   {
11967     tape_action[i] = stored_player[i].effective_action;
11968
11969     /* (this may happen in the RND game engine if a player was not present on
11970        the playfield on level start, but appeared later from a custom element */
11971     if (setup.team_mode &&
11972         tape.recording &&
11973         tape_action[i] &&
11974         !tape.player_participates[i])
11975       tape.player_participates[i] = TRUE;
11976   }
11977
11978   SetTapeActionFromMouseAction(tape_action,
11979                                &local_player->effective_mouse_action);
11980
11981   // only record actions from input devices, but not programmed actions
11982   if (tape.recording)
11983     TapeRecordAction(tape_action);
11984
11985   // remember if game was played (especially after tape stopped playing)
11986   if (!tape.playing && summarized_player_action && !checkGameFailed())
11987     game.GamePlayed = TRUE;
11988
11989 #if USE_NEW_PLAYER_ASSIGNMENTS
11990   // !!! also map player actions in single player mode !!!
11991   // if (game.team_mode)
11992   if (1)
11993   {
11994     byte mapped_action[MAX_PLAYERS];
11995
11996 #if DEBUG_PLAYER_ACTIONS
11997     for (i = 0; i < MAX_PLAYERS; i++)
11998       DebugContinued("", "%d, ", stored_player[i].effective_action);
11999 #endif
12000
12001     for (i = 0; i < MAX_PLAYERS; i++)
12002       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
12003
12004     for (i = 0; i < MAX_PLAYERS; i++)
12005       stored_player[i].effective_action = mapped_action[i];
12006
12007 #if DEBUG_PLAYER_ACTIONS
12008     DebugContinued("", "=> ");
12009     for (i = 0; i < MAX_PLAYERS; i++)
12010       DebugContinued("", "%d, ", stored_player[i].effective_action);
12011     DebugContinued("game:playing:player", "\n");
12012 #endif
12013   }
12014 #if DEBUG_PLAYER_ACTIONS
12015   else
12016   {
12017     for (i = 0; i < MAX_PLAYERS; i++)
12018       DebugContinued("", "%d, ", stored_player[i].effective_action);
12019     DebugContinued("game:playing:player", "\n");
12020   }
12021 #endif
12022 #endif
12023
12024   for (i = 0; i < MAX_PLAYERS; i++)
12025   {
12026     // allow engine snapshot in case of changed movement attempt
12027     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
12028         (stored_player[i].effective_action & KEY_MOTION))
12029       game.snapshot.changed_action = TRUE;
12030
12031     // allow engine snapshot in case of snapping/dropping attempt
12032     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
12033         (stored_player[i].effective_action & KEY_BUTTON) != 0)
12034       game.snapshot.changed_action = TRUE;
12035
12036     game.snapshot.last_action[i] = stored_player[i].effective_action;
12037   }
12038
12039   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12040   {
12041     GameActions_EM_Main();
12042   }
12043   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12044   {
12045     GameActions_SP_Main();
12046   }
12047   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
12048   {
12049     GameActions_MM_Main();
12050   }
12051   else
12052   {
12053     GameActions_RND_Main();
12054   }
12055
12056   BlitScreenToBitmap(backbuffer);
12057
12058   CheckLevelSolved();
12059   CheckLevelTime();
12060
12061   AdvanceFrameAndPlayerCounters(-1);    // advance counters for all players
12062
12063   if (global.show_frames_per_second)
12064   {
12065     static unsigned int fps_counter = 0;
12066     static int fps_frames = 0;
12067     unsigned int fps_delay_ms = Counter() - fps_counter;
12068
12069     fps_frames++;
12070
12071     if (fps_delay_ms >= 500)    // calculate FPS every 0.5 seconds
12072     {
12073       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
12074
12075       fps_frames = 0;
12076       fps_counter = Counter();
12077
12078       // always draw FPS to screen after FPS value was updated
12079       redraw_mask |= REDRAW_FPS;
12080     }
12081
12082     // only draw FPS if no screen areas are deactivated (invisible warp mode)
12083     if (GetDrawDeactivationMask() == REDRAW_NONE)
12084       redraw_mask |= REDRAW_FPS;
12085   }
12086 }
12087
12088 static void GameActions_CheckSaveEngineSnapshot(void)
12089 {
12090   if (!game.snapshot.save_snapshot)
12091     return;
12092
12093   // clear flag for saving snapshot _before_ saving snapshot
12094   game.snapshot.save_snapshot = FALSE;
12095
12096   SaveEngineSnapshotToList();
12097 }
12098
12099 void GameActions(void)
12100 {
12101   GameActionsExt();
12102
12103   GameActions_CheckSaveEngineSnapshot();
12104 }
12105
12106 void GameActions_EM_Main(void)
12107 {
12108   byte effective_action[MAX_PLAYERS];
12109   int i;
12110
12111   for (i = 0; i < MAX_PLAYERS; i++)
12112     effective_action[i] = stored_player[i].effective_action;
12113
12114   GameActions_EM(effective_action);
12115 }
12116
12117 void GameActions_SP_Main(void)
12118 {
12119   byte effective_action[MAX_PLAYERS];
12120   int i;
12121
12122   for (i = 0; i < MAX_PLAYERS; i++)
12123     effective_action[i] = stored_player[i].effective_action;
12124
12125   GameActions_SP(effective_action);
12126
12127   for (i = 0; i < MAX_PLAYERS; i++)
12128   {
12129     if (stored_player[i].force_dropping)
12130       stored_player[i].action |= KEY_BUTTON_DROP;
12131
12132     stored_player[i].force_dropping = FALSE;
12133   }
12134 }
12135
12136 void GameActions_MM_Main(void)
12137 {
12138   AdvanceGfxFrame();
12139
12140   GameActions_MM(local_player->effective_mouse_action);
12141 }
12142
12143 void GameActions_RND_Main(void)
12144 {
12145   GameActions_RND();
12146 }
12147
12148 void GameActions_RND(void)
12149 {
12150   static struct MouseActionInfo mouse_action_last = { 0 };
12151   struct MouseActionInfo mouse_action = local_player->effective_mouse_action;
12152   int magic_wall_x = 0, magic_wall_y = 0;
12153   int i, x, y, element, graphic, last_gfx_frame;
12154
12155   InitPlayfieldScanModeVars();
12156
12157   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12158   {
12159     SCAN_PLAYFIELD(x, y)
12160     {
12161       ChangeCount[x][y] = 0;
12162       ChangeEvent[x][y] = -1;
12163     }
12164   }
12165
12166   if (game.set_centered_player)
12167   {
12168     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12169
12170     // switching to "all players" only possible if all players fit to screen
12171     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12172     {
12173       game.centered_player_nr_next = game.centered_player_nr;
12174       game.set_centered_player = FALSE;
12175     }
12176
12177     // do not switch focus to non-existing (or non-active) player
12178     if (game.centered_player_nr_next >= 0 &&
12179         !stored_player[game.centered_player_nr_next].active)
12180     {
12181       game.centered_player_nr_next = game.centered_player_nr;
12182       game.set_centered_player = FALSE;
12183     }
12184   }
12185
12186   if (game.set_centered_player &&
12187       ScreenMovPos == 0)        // screen currently aligned at tile position
12188   {
12189     int sx, sy;
12190
12191     if (game.centered_player_nr_next == -1)
12192     {
12193       setScreenCenteredToAllPlayers(&sx, &sy);
12194     }
12195     else
12196     {
12197       sx = stored_player[game.centered_player_nr_next].jx;
12198       sy = stored_player[game.centered_player_nr_next].jy;
12199     }
12200
12201     game.centered_player_nr = game.centered_player_nr_next;
12202     game.set_centered_player = FALSE;
12203
12204     DrawRelocateScreen(0, 0, sx, sy, TRUE, setup.quick_switch);
12205     DrawGameDoorValues();
12206   }
12207
12208   // check single step mode (set flag and clear again if any player is active)
12209   game.enter_single_step_mode =
12210     (tape.single_step && tape.recording && !tape.pausing);
12211
12212   for (i = 0; i < MAX_PLAYERS; i++)
12213   {
12214     int actual_player_action = stored_player[i].effective_action;
12215
12216 #if 1
12217     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12218        - rnd_equinox_tetrachloride 048
12219        - rnd_equinox_tetrachloride_ii 096
12220        - rnd_emanuel_schmieg 002
12221        - doctor_sloan_ww 001, 020
12222     */
12223     if (stored_player[i].MovPos == 0)
12224       CheckGravityMovement(&stored_player[i]);
12225 #endif
12226
12227     // overwrite programmed action with tape action
12228     if (stored_player[i].programmed_action)
12229       actual_player_action = stored_player[i].programmed_action;
12230
12231     PlayerActions(&stored_player[i], actual_player_action);
12232
12233     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12234   }
12235
12236   // single step pause mode may already have been toggled by "ScrollPlayer()"
12237   if (game.enter_single_step_mode && !tape.pausing)
12238     TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12239
12240   ScrollScreen(NULL, SCROLL_GO_ON);
12241
12242   /* for backwards compatibility, the following code emulates a fixed bug that
12243      occured when pushing elements (causing elements that just made their last
12244      pushing step to already (if possible) make their first falling step in the
12245      same game frame, which is bad); this code is also needed to use the famous
12246      "spring push bug" which is used in older levels and might be wanted to be
12247      used also in newer levels, but in this case the buggy pushing code is only
12248      affecting the "spring" element and no other elements */
12249
12250   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12251   {
12252     for (i = 0; i < MAX_PLAYERS; i++)
12253     {
12254       struct PlayerInfo *player = &stored_player[i];
12255       int x = player->jx;
12256       int y = player->jy;
12257
12258       if (player->active && player->is_pushing && player->is_moving &&
12259           IS_MOVING(x, y) &&
12260           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12261            Tile[x][y] == EL_SPRING))
12262       {
12263         ContinueMoving(x, y);
12264
12265         // continue moving after pushing (this is actually a bug)
12266         if (!IS_MOVING(x, y))
12267           Stop[x][y] = FALSE;
12268       }
12269     }
12270   }
12271
12272   SCAN_PLAYFIELD(x, y)
12273   {
12274     Last[x][y] = Tile[x][y];
12275
12276     ChangeCount[x][y] = 0;
12277     ChangeEvent[x][y] = -1;
12278
12279     // this must be handled before main playfield loop
12280     if (Tile[x][y] == EL_PLAYER_IS_LEAVING)
12281     {
12282       MovDelay[x][y]--;
12283       if (MovDelay[x][y] <= 0)
12284         RemoveField(x, y);
12285     }
12286
12287     if (Tile[x][y] == EL_ELEMENT_SNAPPING)
12288     {
12289       MovDelay[x][y]--;
12290       if (MovDelay[x][y] <= 0)
12291       {
12292         int element = Store[x][y];
12293         int move_direction = MovDir[x][y];
12294         int player_index_bit = Store2[x][y];
12295
12296         Store[x][y] = 0;
12297         Store2[x][y] = 0;
12298
12299         RemoveField(x, y);
12300         TEST_DrawLevelField(x, y);
12301
12302         TestFieldAfterSnapping(x, y, element, move_direction, player_index_bit);
12303
12304         if (IS_ENVELOPE(element))
12305           local_player->show_envelope = element;
12306       }
12307     }
12308
12309 #if DEBUG
12310     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12311     {
12312       Debug("game:playing:GameActions_RND", "x = %d, y = %d: ChangePage != -1",
12313             x, y);
12314       Debug("game:playing:GameActions_RND", "This should never happen!");
12315
12316       ChangePage[x][y] = -1;
12317     }
12318 #endif
12319
12320     Stop[x][y] = FALSE;
12321     if (WasJustMoving[x][y] > 0)
12322       WasJustMoving[x][y]--;
12323     if (WasJustFalling[x][y] > 0)
12324       WasJustFalling[x][y]--;
12325     if (CheckCollision[x][y] > 0)
12326       CheckCollision[x][y]--;
12327     if (CheckImpact[x][y] > 0)
12328       CheckImpact[x][y]--;
12329
12330     GfxFrame[x][y]++;
12331
12332     /* reset finished pushing action (not done in ContinueMoving() to allow
12333        continuous pushing animation for elements with zero push delay) */
12334     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12335     {
12336       ResetGfxAnimation(x, y);
12337       TEST_DrawLevelField(x, y);
12338     }
12339
12340 #if DEBUG
12341     if (IS_BLOCKED(x, y))
12342     {
12343       int oldx, oldy;
12344
12345       Blocked2Moving(x, y, &oldx, &oldy);
12346       if (!IS_MOVING(oldx, oldy))
12347       {
12348         Debug("game:playing:GameActions_RND", "(BLOCKED => MOVING) context corrupted!");
12349         Debug("game:playing:GameActions_RND", "BLOCKED: x = %d, y = %d", x, y);
12350         Debug("game:playing:GameActions_RND", "!MOVING: oldx = %d, oldy = %d", oldx, oldy);
12351         Debug("game:playing:GameActions_RND", "This should never happen!");
12352       }
12353     }
12354 #endif
12355   }
12356
12357   HandleMouseAction(&mouse_action, &mouse_action_last);
12358
12359   SCAN_PLAYFIELD(x, y)
12360   {
12361     element = Tile[x][y];
12362     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12363     last_gfx_frame = GfxFrame[x][y];
12364
12365     if (element == EL_EMPTY)
12366       graphic = el2img(GfxElementEmpty[x][y]);
12367
12368     ResetGfxFrame(x, y);
12369
12370     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
12371       DrawLevelGraphicAnimation(x, y, graphic);
12372
12373     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12374         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12375       ResetRandomAnimationValue(x, y);
12376
12377     SetRandomAnimationValue(x, y);
12378
12379     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12380
12381     if (IS_INACTIVE(element))
12382     {
12383       if (IS_ANIMATED(graphic))
12384         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12385
12386       continue;
12387     }
12388
12389     // this may take place after moving, so 'element' may have changed
12390     if (IS_CHANGING(x, y) &&
12391         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12392     {
12393       int page = element_info[element].event_page_nr[CE_DELAY];
12394
12395       HandleElementChange(x, y, page);
12396
12397       element = Tile[x][y];
12398       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12399     }
12400
12401     CheckNextToConditions(x, y);
12402
12403     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12404     {
12405       StartMoving(x, y);
12406
12407       element = Tile[x][y];
12408       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12409
12410       if (IS_ANIMATED(graphic) &&
12411           !IS_MOVING(x, y) &&
12412           !Stop[x][y])
12413         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12414
12415       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12416         TEST_DrawTwinkleOnField(x, y);
12417     }
12418     else if (element == EL_ACID)
12419     {
12420       if (!Stop[x][y])
12421         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12422     }
12423     else if ((element == EL_EXIT_OPEN ||
12424               element == EL_EM_EXIT_OPEN ||
12425               element == EL_SP_EXIT_OPEN ||
12426               element == EL_STEEL_EXIT_OPEN ||
12427               element == EL_EM_STEEL_EXIT_OPEN ||
12428               element == EL_SP_TERMINAL ||
12429               element == EL_SP_TERMINAL_ACTIVE ||
12430               element == EL_EXTRA_TIME ||
12431               element == EL_SHIELD_NORMAL ||
12432               element == EL_SHIELD_DEADLY) &&
12433              IS_ANIMATED(graphic))
12434       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12435     else if (IS_MOVING(x, y))
12436       ContinueMoving(x, y);
12437     else if (IS_ACTIVE_BOMB(element))
12438       CheckDynamite(x, y);
12439     else if (element == EL_AMOEBA_GROWING)
12440       AmoebaGrowing(x, y);
12441     else if (element == EL_AMOEBA_SHRINKING)
12442       AmoebaShrinking(x, y);
12443
12444 #if !USE_NEW_AMOEBA_CODE
12445     else if (IS_AMOEBALIVE(element))
12446       AmoebaReproduce(x, y);
12447 #endif
12448
12449     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12450       Life(x, y);
12451     else if (element == EL_EXIT_CLOSED)
12452       CheckExit(x, y);
12453     else if (element == EL_EM_EXIT_CLOSED)
12454       CheckExitEM(x, y);
12455     else if (element == EL_STEEL_EXIT_CLOSED)
12456       CheckExitSteel(x, y);
12457     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12458       CheckExitSteelEM(x, y);
12459     else if (element == EL_SP_EXIT_CLOSED)
12460       CheckExitSP(x, y);
12461     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12462              element == EL_EXPANDABLE_STEELWALL_GROWING)
12463       WallGrowing(x, y);
12464     else if (element == EL_EXPANDABLE_WALL ||
12465              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12466              element == EL_EXPANDABLE_WALL_VERTICAL ||
12467              element == EL_EXPANDABLE_WALL_ANY ||
12468              element == EL_BD_EXPANDABLE_WALL ||
12469              element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12470              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12471              element == EL_EXPANDABLE_STEELWALL_ANY)
12472       CheckWallGrowing(x, y);
12473     else if (element == EL_FLAMES)
12474       CheckForDragon(x, y);
12475     else if (element == EL_EXPLOSION)
12476       ; // drawing of correct explosion animation is handled separately
12477     else if (element == EL_ELEMENT_SNAPPING ||
12478              element == EL_DIAGONAL_SHRINKING ||
12479              element == EL_DIAGONAL_GROWING)
12480     {
12481       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y], GfxDir[x][y]);
12482
12483       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12484     }
12485     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12486       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12487
12488     if (IS_BELT_ACTIVE(element))
12489       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12490
12491     if (game.magic_wall_active)
12492     {
12493       int jx = local_player->jx, jy = local_player->jy;
12494
12495       // play the element sound at the position nearest to the player
12496       if ((element == EL_MAGIC_WALL_FULL ||
12497            element == EL_MAGIC_WALL_ACTIVE ||
12498            element == EL_MAGIC_WALL_EMPTYING ||
12499            element == EL_BD_MAGIC_WALL_FULL ||
12500            element == EL_BD_MAGIC_WALL_ACTIVE ||
12501            element == EL_BD_MAGIC_WALL_EMPTYING ||
12502            element == EL_DC_MAGIC_WALL_FULL ||
12503            element == EL_DC_MAGIC_WALL_ACTIVE ||
12504            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12505           ABS(x - jx) + ABS(y - jy) <
12506           ABS(magic_wall_x - jx) + ABS(magic_wall_y - jy))
12507       {
12508         magic_wall_x = x;
12509         magic_wall_y = y;
12510       }
12511     }
12512   }
12513
12514 #if USE_NEW_AMOEBA_CODE
12515   // new experimental amoeba growth stuff
12516   if (!(FrameCounter % 8))
12517   {
12518     static unsigned int random = 1684108901;
12519
12520     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12521     {
12522       x = RND(lev_fieldx);
12523       y = RND(lev_fieldy);
12524       element = Tile[x][y];
12525
12526       if (!IS_PLAYER(x, y) &&
12527           (element == EL_EMPTY ||
12528            CAN_GROW_INTO(element) ||
12529            element == EL_QUICKSAND_EMPTY ||
12530            element == EL_QUICKSAND_FAST_EMPTY ||
12531            element == EL_ACID_SPLASH_LEFT ||
12532            element == EL_ACID_SPLASH_RIGHT))
12533       {
12534         if ((IN_LEV_FIELD(x, y - 1) && Tile[x][y - 1] == EL_AMOEBA_WET) ||
12535             (IN_LEV_FIELD(x - 1, y) && Tile[x - 1][y] == EL_AMOEBA_WET) ||
12536             (IN_LEV_FIELD(x + 1, y) && Tile[x + 1][y] == EL_AMOEBA_WET) ||
12537             (IN_LEV_FIELD(x, y + 1) && Tile[x][y + 1] == EL_AMOEBA_WET))
12538           Tile[x][y] = EL_AMOEBA_DROP;
12539       }
12540
12541       random = random * 129 + 1;
12542     }
12543   }
12544 #endif
12545
12546   game.explosions_delayed = FALSE;
12547
12548   SCAN_PLAYFIELD(x, y)
12549   {
12550     element = Tile[x][y];
12551
12552     if (ExplodeField[x][y])
12553       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12554     else if (element == EL_EXPLOSION)
12555       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12556
12557     ExplodeField[x][y] = EX_TYPE_NONE;
12558   }
12559
12560   game.explosions_delayed = TRUE;
12561
12562   if (game.magic_wall_active)
12563   {
12564     if (!(game.magic_wall_time_left % 4))
12565     {
12566       int element = Tile[magic_wall_x][magic_wall_y];
12567
12568       if (element == EL_BD_MAGIC_WALL_FULL ||
12569           element == EL_BD_MAGIC_WALL_ACTIVE ||
12570           element == EL_BD_MAGIC_WALL_EMPTYING)
12571         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12572       else if (element == EL_DC_MAGIC_WALL_FULL ||
12573                element == EL_DC_MAGIC_WALL_ACTIVE ||
12574                element == EL_DC_MAGIC_WALL_EMPTYING)
12575         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12576       else
12577         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12578     }
12579
12580     if (game.magic_wall_time_left > 0)
12581     {
12582       game.magic_wall_time_left--;
12583
12584       if (!game.magic_wall_time_left)
12585       {
12586         SCAN_PLAYFIELD(x, y)
12587         {
12588           element = Tile[x][y];
12589
12590           if (element == EL_MAGIC_WALL_ACTIVE ||
12591               element == EL_MAGIC_WALL_FULL)
12592           {
12593             Tile[x][y] = EL_MAGIC_WALL_DEAD;
12594             TEST_DrawLevelField(x, y);
12595           }
12596           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12597                    element == EL_BD_MAGIC_WALL_FULL)
12598           {
12599             Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
12600             TEST_DrawLevelField(x, y);
12601           }
12602           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12603                    element == EL_DC_MAGIC_WALL_FULL)
12604           {
12605             Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
12606             TEST_DrawLevelField(x, y);
12607           }
12608         }
12609
12610         game.magic_wall_active = FALSE;
12611       }
12612     }
12613   }
12614
12615   if (game.light_time_left > 0)
12616   {
12617     game.light_time_left--;
12618
12619     if (game.light_time_left == 0)
12620       RedrawAllLightSwitchesAndInvisibleElements();
12621   }
12622
12623   if (game.timegate_time_left > 0)
12624   {
12625     game.timegate_time_left--;
12626
12627     if (game.timegate_time_left == 0)
12628       CloseAllOpenTimegates();
12629   }
12630
12631   if (game.lenses_time_left > 0)
12632   {
12633     game.lenses_time_left--;
12634
12635     if (game.lenses_time_left == 0)
12636       RedrawAllInvisibleElementsForLenses();
12637   }
12638
12639   if (game.magnify_time_left > 0)
12640   {
12641     game.magnify_time_left--;
12642
12643     if (game.magnify_time_left == 0)
12644       RedrawAllInvisibleElementsForMagnifier();
12645   }
12646
12647   for (i = 0; i < MAX_PLAYERS; i++)
12648   {
12649     struct PlayerInfo *player = &stored_player[i];
12650
12651     if (SHIELD_ON(player))
12652     {
12653       if (player->shield_deadly_time_left)
12654         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12655       else if (player->shield_normal_time_left)
12656         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12657     }
12658   }
12659
12660 #if USE_DELAYED_GFX_REDRAW
12661   SCAN_PLAYFIELD(x, y)
12662   {
12663     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12664     {
12665       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12666          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12667
12668       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12669         DrawLevelField(x, y);
12670
12671       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12672         DrawLevelFieldCrumbled(x, y);
12673
12674       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12675         DrawLevelFieldCrumbledNeighbours(x, y);
12676
12677       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12678         DrawTwinkleOnField(x, y);
12679     }
12680
12681     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12682   }
12683 #endif
12684
12685   DrawAllPlayers();
12686   PlayAllPlayersSound();
12687
12688   for (i = 0; i < MAX_PLAYERS; i++)
12689   {
12690     struct PlayerInfo *player = &stored_player[i];
12691
12692     if (player->show_envelope != 0 && (!player->active ||
12693                                        player->MovPos == 0))
12694     {
12695       ShowEnvelope(player->show_envelope - EL_ENVELOPE_1);
12696
12697       player->show_envelope = 0;
12698     }
12699   }
12700
12701   // use random number generator in every frame to make it less predictable
12702   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12703     RND(1);
12704
12705   mouse_action_last = mouse_action;
12706 }
12707
12708 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12709 {
12710   int min_x = x, min_y = y, max_x = x, max_y = y;
12711   int scr_fieldx = getScreenFieldSizeX();
12712   int scr_fieldy = getScreenFieldSizeY();
12713   int i;
12714
12715   for (i = 0; i < MAX_PLAYERS; i++)
12716   {
12717     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12718
12719     if (!stored_player[i].active || &stored_player[i] == player)
12720       continue;
12721
12722     min_x = MIN(min_x, jx);
12723     min_y = MIN(min_y, jy);
12724     max_x = MAX(max_x, jx);
12725     max_y = MAX(max_y, jy);
12726   }
12727
12728   return (max_x - min_x < scr_fieldx && max_y - min_y < scr_fieldy);
12729 }
12730
12731 static boolean AllPlayersInVisibleScreen(void)
12732 {
12733   int i;
12734
12735   for (i = 0; i < MAX_PLAYERS; i++)
12736   {
12737     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12738
12739     if (!stored_player[i].active)
12740       continue;
12741
12742     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12743       return FALSE;
12744   }
12745
12746   return TRUE;
12747 }
12748
12749 void ScrollLevel(int dx, int dy)
12750 {
12751   int scroll_offset = 2 * TILEX_VAR;
12752   int x, y;
12753
12754   BlitBitmap(drawto_field, drawto_field,
12755              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12756              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12757              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12758              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12759              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12760              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12761
12762   if (dx != 0)
12763   {
12764     x = (dx == 1 ? BX1 : BX2);
12765     for (y = BY1; y <= BY2; y++)
12766       DrawScreenField(x, y);
12767   }
12768
12769   if (dy != 0)
12770   {
12771     y = (dy == 1 ? BY1 : BY2);
12772     for (x = BX1; x <= BX2; x++)
12773       DrawScreenField(x, y);
12774   }
12775
12776   redraw_mask |= REDRAW_FIELD;
12777 }
12778
12779 static boolean canFallDown(struct PlayerInfo *player)
12780 {
12781   int jx = player->jx, jy = player->jy;
12782
12783   return (IN_LEV_FIELD(jx, jy + 1) &&
12784           (IS_FREE(jx, jy + 1) ||
12785            (Tile[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12786           IS_WALKABLE_FROM(Tile[jx][jy], MV_DOWN) &&
12787           !IS_WALKABLE_INSIDE(Tile[jx][jy]));
12788 }
12789
12790 static boolean canPassField(int x, int y, int move_dir)
12791 {
12792   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12793   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12794   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12795   int nextx = x + dx;
12796   int nexty = y + dy;
12797   int element = Tile[x][y];
12798
12799   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12800           !CAN_MOVE(element) &&
12801           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12802           IS_WALKABLE_FROM(Tile[nextx][nexty], move_dir) &&
12803           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12804 }
12805
12806 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12807 {
12808   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12809   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12810   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12811   int newx = x + dx;
12812   int newy = y + dy;
12813
12814   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12815           IS_GRAVITY_REACHABLE(Tile[newx][newy]) &&
12816           (IS_DIGGABLE(Tile[newx][newy]) ||
12817            IS_WALKABLE_FROM(Tile[newx][newy], opposite_dir) ||
12818            canPassField(newx, newy, move_dir)));
12819 }
12820
12821 static void CheckGravityMovement(struct PlayerInfo *player)
12822 {
12823   if (player->gravity && !player->programmed_action)
12824   {
12825     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12826     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12827     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12828     int jx = player->jx, jy = player->jy;
12829     boolean player_is_moving_to_valid_field =
12830       (!player_is_snapping &&
12831        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12832         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12833     boolean player_can_fall_down = canFallDown(player);
12834
12835     if (player_can_fall_down &&
12836         !player_is_moving_to_valid_field)
12837       player->programmed_action = MV_DOWN;
12838   }
12839 }
12840
12841 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12842 {
12843   return CheckGravityMovement(player);
12844
12845   if (player->gravity && !player->programmed_action)
12846   {
12847     int jx = player->jx, jy = player->jy;
12848     boolean field_under_player_is_free =
12849       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12850     boolean player_is_standing_on_valid_field =
12851       (IS_WALKABLE_INSIDE(Tile[jx][jy]) ||
12852        (IS_WALKABLE(Tile[jx][jy]) &&
12853         !(element_info[Tile[jx][jy]].access_direction & MV_DOWN)));
12854
12855     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12856       player->programmed_action = MV_DOWN;
12857   }
12858 }
12859
12860 /*
12861   MovePlayerOneStep()
12862   -----------------------------------------------------------------------------
12863   dx, dy:               direction (non-diagonal) to try to move the player to
12864   real_dx, real_dy:     direction as read from input device (can be diagonal)
12865 */
12866
12867 boolean MovePlayerOneStep(struct PlayerInfo *player,
12868                           int dx, int dy, int real_dx, int real_dy)
12869 {
12870   int jx = player->jx, jy = player->jy;
12871   int new_jx = jx + dx, new_jy = jy + dy;
12872   int can_move;
12873   boolean player_can_move = !player->cannot_move;
12874
12875   if (!player->active || (!dx && !dy))
12876     return MP_NO_ACTION;
12877
12878   player->MovDir = (dx < 0 ? MV_LEFT :
12879                     dx > 0 ? MV_RIGHT :
12880                     dy < 0 ? MV_UP :
12881                     dy > 0 ? MV_DOWN :  MV_NONE);
12882
12883   if (!IN_LEV_FIELD(new_jx, new_jy))
12884     return MP_NO_ACTION;
12885
12886   if (!player_can_move)
12887   {
12888     if (player->MovPos == 0)
12889     {
12890       player->is_moving = FALSE;
12891       player->is_digging = FALSE;
12892       player->is_collecting = FALSE;
12893       player->is_snapping = FALSE;
12894       player->is_pushing = FALSE;
12895     }
12896   }
12897
12898   if (!network.enabled && game.centered_player_nr == -1 &&
12899       !AllPlayersInSight(player, new_jx, new_jy))
12900     return MP_NO_ACTION;
12901
12902   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx, real_dy, DF_DIG);
12903   if (can_move != MP_MOVING)
12904     return can_move;
12905
12906   // check if DigField() has caused relocation of the player
12907   if (player->jx != jx || player->jy != jy)
12908     return MP_NO_ACTION;        // <-- !!! CHECK THIS [-> MP_ACTION ?] !!!
12909
12910   StorePlayer[jx][jy] = 0;
12911   player->last_jx = jx;
12912   player->last_jy = jy;
12913   player->jx = new_jx;
12914   player->jy = new_jy;
12915   StorePlayer[new_jx][new_jy] = player->element_nr;
12916
12917   if (player->move_delay_value_next != -1)
12918   {
12919     player->move_delay_value = player->move_delay_value_next;
12920     player->move_delay_value_next = -1;
12921   }
12922
12923   player->MovPos =
12924     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12925
12926   player->step_counter++;
12927
12928   PlayerVisit[jx][jy] = FrameCounter;
12929
12930   player->is_moving = TRUE;
12931
12932 #if 1
12933   // should better be called in MovePlayer(), but this breaks some tapes
12934   ScrollPlayer(player, SCROLL_INIT);
12935 #endif
12936
12937   return MP_MOVING;
12938 }
12939
12940 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12941 {
12942   int jx = player->jx, jy = player->jy;
12943   int old_jx = jx, old_jy = jy;
12944   int moved = MP_NO_ACTION;
12945
12946   if (!player->active)
12947     return FALSE;
12948
12949   if (!dx && !dy)
12950   {
12951     if (player->MovPos == 0)
12952     {
12953       player->is_moving = FALSE;
12954       player->is_digging = FALSE;
12955       player->is_collecting = FALSE;
12956       player->is_snapping = FALSE;
12957       player->is_pushing = FALSE;
12958     }
12959
12960     return FALSE;
12961   }
12962
12963   if (player->move_delay > 0)
12964     return FALSE;
12965
12966   player->move_delay = -1;              // set to "uninitialized" value
12967
12968   // store if player is automatically moved to next field
12969   player->is_auto_moving = (player->programmed_action != MV_NONE);
12970
12971   // remove the last programmed player action
12972   player->programmed_action = 0;
12973
12974   if (player->MovPos)
12975   {
12976     // should only happen if pre-1.2 tape recordings are played
12977     // this is only for backward compatibility
12978
12979     int original_move_delay_value = player->move_delay_value;
12980
12981 #if DEBUG
12982     Debug("game:playing:MovePlayer",
12983           "THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]",
12984           tape.counter);
12985 #endif
12986
12987     // scroll remaining steps with finest movement resolution
12988     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12989
12990     while (player->MovPos)
12991     {
12992       ScrollPlayer(player, SCROLL_GO_ON);
12993       ScrollScreen(NULL, SCROLL_GO_ON);
12994
12995       AdvanceFrameAndPlayerCounters(player->index_nr);
12996
12997       DrawAllPlayers();
12998       BackToFront_WithFrameDelay(0);
12999     }
13000
13001     player->move_delay_value = original_move_delay_value;
13002   }
13003
13004   player->is_active = FALSE;
13005
13006   if (player->last_move_dir & MV_HORIZONTAL)
13007   {
13008     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
13009       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
13010   }
13011   else
13012   {
13013     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
13014       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
13015   }
13016
13017   if (!moved && !player->is_active)
13018   {
13019     player->is_moving = FALSE;
13020     player->is_digging = FALSE;
13021     player->is_collecting = FALSE;
13022     player->is_snapping = FALSE;
13023     player->is_pushing = FALSE;
13024   }
13025
13026   jx = player->jx;
13027   jy = player->jy;
13028
13029   if (moved & MP_MOVING && !ScreenMovPos &&
13030       (player->index_nr == game.centered_player_nr ||
13031        game.centered_player_nr == -1))
13032   {
13033     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
13034
13035     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13036     {
13037       // actual player has left the screen -- scroll in that direction
13038       if (jx != old_jx)         // player has moved horizontally
13039         scroll_x += (jx - old_jx);
13040       else                      // player has moved vertically
13041         scroll_y += (jy - old_jy);
13042     }
13043     else
13044     {
13045       int offset_raw = game.scroll_delay_value;
13046
13047       if (jx != old_jx)         // player has moved horizontally
13048       {
13049         int offset = MIN(offset_raw, (SCR_FIELDX - 2) / 2);
13050         int offset_x = offset * (player->MovDir == MV_LEFT ? +1 : -1);
13051         int new_scroll_x = jx - MIDPOSX + offset_x;
13052
13053         if ((player->MovDir == MV_LEFT  && scroll_x > new_scroll_x) ||
13054             (player->MovDir == MV_RIGHT && scroll_x < new_scroll_x))
13055           scroll_x = new_scroll_x;
13056
13057         // don't scroll over playfield boundaries
13058         scroll_x = MIN(MAX(SBX_Left, scroll_x), SBX_Right);
13059
13060         // don't scroll more than one field at a time
13061         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13062
13063         // don't scroll against the player's moving direction
13064         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
13065             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13066           scroll_x = old_scroll_x;
13067       }
13068       else                      // player has moved vertically
13069       {
13070         int offset = MIN(offset_raw, (SCR_FIELDY - 2) / 2);
13071         int offset_y = offset * (player->MovDir == MV_UP ? +1 : -1);
13072         int new_scroll_y = jy - MIDPOSY + offset_y;
13073
13074         if ((player->MovDir == MV_UP   && scroll_y > new_scroll_y) ||
13075             (player->MovDir == MV_DOWN && scroll_y < new_scroll_y))
13076           scroll_y = new_scroll_y;
13077
13078         // don't scroll over playfield boundaries
13079         scroll_y = MIN(MAX(SBY_Upper, scroll_y), SBY_Lower);
13080
13081         // don't scroll more than one field at a time
13082         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13083
13084         // don't scroll against the player's moving direction
13085         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
13086             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13087           scroll_y = old_scroll_y;
13088       }
13089     }
13090
13091     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13092     {
13093       if (!network.enabled && game.centered_player_nr == -1 &&
13094           !AllPlayersInVisibleScreen())
13095       {
13096         scroll_x = old_scroll_x;
13097         scroll_y = old_scroll_y;
13098       }
13099       else
13100       {
13101         ScrollScreen(player, SCROLL_INIT);
13102         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13103       }
13104     }
13105   }
13106
13107   player->StepFrame = 0;
13108
13109   if (moved & MP_MOVING)
13110   {
13111     if (old_jx != jx && old_jy == jy)
13112       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13113     else if (old_jx == jx && old_jy != jy)
13114       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13115
13116     TEST_DrawLevelField(jx, jy);        // for "crumbled sand"
13117
13118     player->last_move_dir = player->MovDir;
13119     player->is_moving = TRUE;
13120     player->is_snapping = FALSE;
13121     player->is_switching = FALSE;
13122     player->is_dropping = FALSE;
13123     player->is_dropping_pressed = FALSE;
13124     player->drop_pressed_delay = 0;
13125
13126 #if 0
13127     // should better be called here than above, but this breaks some tapes
13128     ScrollPlayer(player, SCROLL_INIT);
13129 #endif
13130   }
13131   else
13132   {
13133     CheckGravityMovementWhenNotMoving(player);
13134
13135     player->is_moving = FALSE;
13136
13137     /* at this point, the player is allowed to move, but cannot move right now
13138        (e.g. because of something blocking the way) -- ensure that the player
13139        is also allowed to move in the next frame (in old versions before 3.1.1,
13140        the player was forced to wait again for eight frames before next try) */
13141
13142     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13143       player->move_delay = 0;   // allow direct movement in the next frame
13144   }
13145
13146   if (player->move_delay == -1)         // not yet initialized by DigField()
13147     player->move_delay = player->move_delay_value;
13148
13149   if (game.engine_version < VERSION_IDENT(3,0,7,0))
13150   {
13151     TestIfPlayerTouchesBadThing(jx, jy);
13152     TestIfPlayerTouchesCustomElement(jx, jy);
13153   }
13154
13155   if (!player->active)
13156     RemovePlayer(player);
13157
13158   return moved;
13159 }
13160
13161 void ScrollPlayer(struct PlayerInfo *player, int mode)
13162 {
13163   int jx = player->jx, jy = player->jy;
13164   int last_jx = player->last_jx, last_jy = player->last_jy;
13165   int move_stepsize = TILEX / player->move_delay_value;
13166
13167   if (!player->active)
13168     return;
13169
13170   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      // player not moving
13171     return;
13172
13173   if (mode == SCROLL_INIT)
13174   {
13175     player->actual_frame_counter.count = FrameCounter;
13176     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13177
13178     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13179         Tile[last_jx][last_jy] == EL_EMPTY)
13180     {
13181       int last_field_block_delay = 0;   // start with no blocking at all
13182       int block_delay_adjustment = player->block_delay_adjustment;
13183
13184       // if player blocks last field, add delay for exactly one move
13185       if (player->block_last_field)
13186       {
13187         last_field_block_delay += player->move_delay_value;
13188
13189         // when blocking enabled, prevent moving up despite gravity
13190         if (player->gravity && player->MovDir == MV_UP)
13191           block_delay_adjustment = -1;
13192       }
13193
13194       // add block delay adjustment (also possible when not blocking)
13195       last_field_block_delay += block_delay_adjustment;
13196
13197       Tile[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13198       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13199     }
13200
13201     if (player->MovPos != 0)    // player has not yet reached destination
13202       return;
13203   }
13204   else if (!FrameReached(&player->actual_frame_counter))
13205     return;
13206
13207   if (player->MovPos != 0)
13208   {
13209     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13210     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13211
13212     // before DrawPlayer() to draw correct player graphic for this case
13213     if (player->MovPos == 0)
13214       CheckGravityMovement(player);
13215   }
13216
13217   if (player->MovPos == 0)      // player reached destination field
13218   {
13219     if (player->move_delay_reset_counter > 0)
13220     {
13221       player->move_delay_reset_counter--;
13222
13223       if (player->move_delay_reset_counter == 0)
13224       {
13225         // continue with normal speed after quickly moving through gate
13226         HALVE_PLAYER_SPEED(player);
13227
13228         // be able to make the next move without delay
13229         player->move_delay = 0;
13230       }
13231     }
13232
13233     if (Tile[jx][jy] == EL_EXIT_OPEN ||
13234         Tile[jx][jy] == EL_EM_EXIT_OPEN ||
13235         Tile[jx][jy] == EL_EM_EXIT_OPENING ||
13236         Tile[jx][jy] == EL_STEEL_EXIT_OPEN ||
13237         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13238         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
13239         Tile[jx][jy] == EL_SP_EXIT_OPEN ||
13240         Tile[jx][jy] == EL_SP_EXIT_OPENING)     // <-- special case
13241     {
13242       ExitPlayer(player);
13243
13244       if (game.players_still_needed == 0 &&
13245           (game.friends_still_needed == 0 ||
13246            IS_SP_ELEMENT(Tile[jx][jy])))
13247         LevelSolved();
13248     }
13249
13250     player->last_jx = jx;
13251     player->last_jy = jy;
13252
13253     // this breaks one level: "machine", level 000
13254     {
13255       int move_direction = player->MovDir;
13256       int enter_side = MV_DIR_OPPOSITE(move_direction);
13257       int leave_side = move_direction;
13258       int old_jx = last_jx;
13259       int old_jy = last_jy;
13260       int old_element = Tile[old_jx][old_jy];
13261       int new_element = Tile[jx][jy];
13262
13263       if (IS_CUSTOM_ELEMENT(old_element))
13264         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13265                                    CE_LEFT_BY_PLAYER,
13266                                    player->index_bit, leave_side);
13267
13268       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13269                                           CE_PLAYER_LEAVES_X,
13270                                           player->index_bit, leave_side);
13271
13272       // needed because pushed element has not yet reached its destination,
13273       // so it would trigger a change event at its previous field location
13274       if (!player->is_pushing)
13275       {
13276         if (IS_CUSTOM_ELEMENT(new_element))
13277           CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13278                                      player->index_bit, enter_side);
13279
13280         CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13281                                             CE_PLAYER_ENTERS_X,
13282                                             player->index_bit, enter_side);
13283       }
13284
13285       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13286                                         CE_MOVE_OF_X, move_direction);
13287     }
13288
13289     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13290     {
13291       TestIfPlayerTouchesBadThing(jx, jy);
13292       TestIfPlayerTouchesCustomElement(jx, jy);
13293
13294       // needed because pushed element has not yet reached its destination,
13295       // so it would trigger a change event at its previous field location
13296       if (!player->is_pushing)
13297         TestIfElementTouchesCustomElement(jx, jy);      // for empty space
13298
13299       if (level.finish_dig_collect &&
13300           (player->is_digging || player->is_collecting))
13301       {
13302         int last_element = player->last_removed_element;
13303         int move_direction = player->MovDir;
13304         int enter_side = MV_DIR_OPPOSITE(move_direction);
13305         int change_event = (player->is_digging ? CE_PLAYER_DIGS_X :
13306                             CE_PLAYER_COLLECTS_X);
13307
13308         CheckTriggeredElementChangeByPlayer(jx, jy, last_element, change_event,
13309                                             player->index_bit, enter_side);
13310
13311         player->last_removed_element = EL_UNDEFINED;
13312       }
13313
13314       if (!player->active)
13315         RemovePlayer(player);
13316     }
13317
13318     if (level.use_step_counter)
13319       CheckLevelTime_StepCounter();
13320
13321     if (tape.single_step && tape.recording && !tape.pausing &&
13322         !player->programmed_action)
13323       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13324
13325     if (!player->programmed_action)
13326       CheckSaveEngineSnapshot(player);
13327   }
13328 }
13329
13330 void ScrollScreen(struct PlayerInfo *player, int mode)
13331 {
13332   static DelayCounter screen_frame_counter = { 0 };
13333
13334   if (mode == SCROLL_INIT)
13335   {
13336     // set scrolling step size according to actual player's moving speed
13337     ScrollStepSize = TILEX / player->move_delay_value;
13338
13339     screen_frame_counter.count = FrameCounter;
13340     screen_frame_counter.value = 1;
13341
13342     ScreenMovDir = player->MovDir;
13343     ScreenMovPos = player->MovPos;
13344     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13345     return;
13346   }
13347   else if (!FrameReached(&screen_frame_counter))
13348     return;
13349
13350   if (ScreenMovPos)
13351   {
13352     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13353     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13354     redraw_mask |= REDRAW_FIELD;
13355   }
13356   else
13357     ScreenMovDir = MV_NONE;
13358 }
13359
13360 void CheckNextToConditions(int x, int y)
13361 {
13362   int element = Tile[x][y];
13363
13364   if (IS_PLAYER(x, y))
13365     TestIfPlayerNextToCustomElement(x, y);
13366
13367   if (CAN_CHANGE_OR_HAS_ACTION(element) &&
13368       HAS_ANY_CHANGE_EVENT(element, CE_NEXT_TO_X))
13369     TestIfElementNextToCustomElement(x, y);
13370 }
13371
13372 void TestIfPlayerNextToCustomElement(int x, int y)
13373 {
13374   struct XY *xy = xy_topdown;
13375   static int trigger_sides[4][2] =
13376   {
13377     // center side       border side
13378     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13379     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13380     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13381     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13382   };
13383   int i;
13384
13385   if (!IS_PLAYER(x, y))
13386     return;
13387
13388   struct PlayerInfo *player = PLAYERINFO(x, y);
13389
13390   if (player->is_moving)
13391     return;
13392
13393   for (i = 0; i < NUM_DIRECTIONS; i++)
13394   {
13395     int xx = x + xy[i].x;
13396     int yy = y + xy[i].y;
13397     int border_side = trigger_sides[i][1];
13398     int border_element;
13399
13400     if (!IN_LEV_FIELD(xx, yy))
13401       continue;
13402
13403     if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
13404       continue;         // center and border element not connected
13405
13406     border_element = Tile[xx][yy];
13407
13408     CheckElementChangeByPlayer(xx, yy, border_element, CE_NEXT_TO_PLAYER,
13409                                player->index_bit, border_side);
13410     CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13411                                         CE_PLAYER_NEXT_TO_X,
13412                                         player->index_bit, border_side);
13413
13414     /* use player element that is initially defined in the level playfield,
13415        not the player element that corresponds to the runtime player number
13416        (example: a level that contains EL_PLAYER_3 as the only player would
13417        incorrectly give EL_PLAYER_1 for "player->element_nr") */
13418
13419     CheckElementChangeBySide(xx, yy, border_element, player->initial_element,
13420                              CE_NEXT_TO_X, border_side);
13421   }
13422 }
13423
13424 void TestIfPlayerTouchesCustomElement(int x, int y)
13425 {
13426   struct XY *xy = xy_topdown;
13427   static int trigger_sides[4][2] =
13428   {
13429     // center side       border side
13430     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13431     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13432     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13433     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13434   };
13435   static int touch_dir[4] =
13436   {
13437     MV_LEFT | MV_RIGHT,
13438     MV_UP   | MV_DOWN,
13439     MV_UP   | MV_DOWN,
13440     MV_LEFT | MV_RIGHT
13441   };
13442   int center_element = Tile[x][y];      // should always be non-moving!
13443   int i;
13444
13445   for (i = 0; i < NUM_DIRECTIONS; i++)
13446   {
13447     int xx = x + xy[i].x;
13448     int yy = y + xy[i].y;
13449     int center_side = trigger_sides[i][0];
13450     int border_side = trigger_sides[i][1];
13451     int border_element;
13452
13453     if (!IN_LEV_FIELD(xx, yy))
13454       continue;
13455
13456     if (IS_PLAYER(x, y))                // player found at center element
13457     {
13458       struct PlayerInfo *player = PLAYERINFO(x, y);
13459
13460       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13461         border_element = Tile[xx][yy];          // may be moving!
13462       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13463         border_element = Tile[xx][yy];
13464       else if (MovDir[xx][yy] & touch_dir[i])   // elements are touching
13465         border_element = MovingOrBlocked2Element(xx, yy);
13466       else
13467         continue;               // center and border element do not touch
13468
13469       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13470                                  player->index_bit, border_side);
13471       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13472                                           CE_PLAYER_TOUCHES_X,
13473                                           player->index_bit, border_side);
13474
13475       {
13476         /* use player element that is initially defined in the level playfield,
13477            not the player element that corresponds to the runtime player number
13478            (example: a level that contains EL_PLAYER_3 as the only player would
13479            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13480         int player_element = PLAYERINFO(x, y)->initial_element;
13481
13482         // as element "X" is the player here, check opposite (center) side
13483         CheckElementChangeBySide(xx, yy, border_element, player_element,
13484                                  CE_TOUCHING_X, center_side);
13485       }
13486     }
13487     else if (IS_PLAYER(xx, yy))         // player found at border element
13488     {
13489       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13490
13491       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13492       {
13493         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13494           continue;             // center and border element do not touch
13495       }
13496
13497       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13498                                  player->index_bit, center_side);
13499       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13500                                           CE_PLAYER_TOUCHES_X,
13501                                           player->index_bit, center_side);
13502
13503       {
13504         /* use player element that is initially defined in the level playfield,
13505            not the player element that corresponds to the runtime player number
13506            (example: a level that contains EL_PLAYER_3 as the only player would
13507            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13508         int player_element = PLAYERINFO(xx, yy)->initial_element;
13509
13510         // as element "X" is the player here, check opposite (border) side
13511         CheckElementChangeBySide(x, y, center_element, player_element,
13512                                  CE_TOUCHING_X, border_side);
13513       }
13514
13515       break;
13516     }
13517   }
13518 }
13519
13520 void TestIfElementNextToCustomElement(int x, int y)
13521 {
13522   struct XY *xy = xy_topdown;
13523   static int trigger_sides[4][2] =
13524   {
13525     // center side      border side
13526     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13527     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13528     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13529     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13530   };
13531   int center_element = Tile[x][y];      // should always be non-moving!
13532   int i;
13533
13534   if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
13535     return;
13536
13537   for (i = 0; i < NUM_DIRECTIONS; i++)
13538   {
13539     int xx = x + xy[i].x;
13540     int yy = y + xy[i].y;
13541     int border_side = trigger_sides[i][1];
13542     int border_element;
13543
13544     if (!IN_LEV_FIELD(xx, yy))
13545       continue;
13546
13547     if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
13548       continue;                 // center and border element not connected
13549
13550     border_element = Tile[xx][yy];
13551
13552     // check for change of center element (but change it only once)
13553     if (CheckElementChangeBySide(x, y, center_element, border_element,
13554                                  CE_NEXT_TO_X, border_side))
13555       break;
13556   }
13557 }
13558
13559 void TestIfElementTouchesCustomElement(int x, int y)
13560 {
13561   struct XY *xy = xy_topdown;
13562   static int trigger_sides[4][2] =
13563   {
13564     // center side      border side
13565     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13566     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13567     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13568     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13569   };
13570   static int touch_dir[4] =
13571   {
13572     MV_LEFT | MV_RIGHT,
13573     MV_UP   | MV_DOWN,
13574     MV_UP   | MV_DOWN,
13575     MV_LEFT | MV_RIGHT
13576   };
13577   boolean change_center_element = FALSE;
13578   int center_element = Tile[x][y];      // should always be non-moving!
13579   int border_element_old[NUM_DIRECTIONS];
13580   int i;
13581
13582   for (i = 0; i < NUM_DIRECTIONS; i++)
13583   {
13584     int xx = x + xy[i].x;
13585     int yy = y + xy[i].y;
13586     int border_element;
13587
13588     border_element_old[i] = -1;
13589
13590     if (!IN_LEV_FIELD(xx, yy))
13591       continue;
13592
13593     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13594       border_element = Tile[xx][yy];    // may be moving!
13595     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13596       border_element = Tile[xx][yy];
13597     else if (MovDir[xx][yy] & touch_dir[i])     // elements are touching
13598       border_element = MovingOrBlocked2Element(xx, yy);
13599     else
13600       continue;                 // center and border element do not touch
13601
13602     border_element_old[i] = border_element;
13603   }
13604
13605   for (i = 0; i < NUM_DIRECTIONS; i++)
13606   {
13607     int xx = x + xy[i].x;
13608     int yy = y + xy[i].y;
13609     int center_side = trigger_sides[i][0];
13610     int border_element = border_element_old[i];
13611
13612     if (border_element == -1)
13613       continue;
13614
13615     // check for change of border element
13616     CheckElementChangeBySide(xx, yy, border_element, center_element,
13617                              CE_TOUCHING_X, center_side);
13618
13619     // (center element cannot be player, so we don't have to check this here)
13620   }
13621
13622   for (i = 0; i < NUM_DIRECTIONS; i++)
13623   {
13624     int xx = x + xy[i].x;
13625     int yy = y + xy[i].y;
13626     int border_side = trigger_sides[i][1];
13627     int border_element = border_element_old[i];
13628
13629     if (border_element == -1)
13630       continue;
13631
13632     // check for change of center element (but change it only once)
13633     if (!change_center_element)
13634       change_center_element =
13635         CheckElementChangeBySide(x, y, center_element, border_element,
13636                                  CE_TOUCHING_X, border_side);
13637
13638     if (IS_PLAYER(xx, yy))
13639     {
13640       /* use player element that is initially defined in the level playfield,
13641          not the player element that corresponds to the runtime player number
13642          (example: a level that contains EL_PLAYER_3 as the only player would
13643          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13644       int player_element = PLAYERINFO(xx, yy)->initial_element;
13645
13646       // as element "X" is the player here, check opposite (border) side
13647       CheckElementChangeBySide(x, y, center_element, player_element,
13648                                CE_TOUCHING_X, border_side);
13649     }
13650   }
13651 }
13652
13653 void TestIfElementHitsCustomElement(int x, int y, int direction)
13654 {
13655   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13656   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13657   int hitx = x + dx, hity = y + dy;
13658   int hitting_element = Tile[x][y];
13659   int touched_element;
13660
13661   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13662     return;
13663
13664   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13665                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13666
13667   if (IN_LEV_FIELD(hitx, hity))
13668   {
13669     int opposite_direction = MV_DIR_OPPOSITE(direction);
13670     int hitting_side = direction;
13671     int touched_side = opposite_direction;
13672     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13673                           MovDir[hitx][hity] != direction ||
13674                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13675
13676     object_hit = TRUE;
13677
13678     if (object_hit)
13679     {
13680       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13681                                CE_HITTING_X, touched_side);
13682
13683       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13684                                CE_HIT_BY_X, hitting_side);
13685
13686       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13687                                CE_HIT_BY_SOMETHING, opposite_direction);
13688
13689       if (IS_PLAYER(hitx, hity))
13690       {
13691         /* use player element that is initially defined in the level playfield,
13692            not the player element that corresponds to the runtime player number
13693            (example: a level that contains EL_PLAYER_3 as the only player would
13694            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13695         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13696
13697         CheckElementChangeBySide(x, y, hitting_element, player_element,
13698                                  CE_HITTING_X, touched_side);
13699       }
13700     }
13701   }
13702
13703   // "hitting something" is also true when hitting the playfield border
13704   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13705                            CE_HITTING_SOMETHING, direction);
13706 }
13707
13708 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13709 {
13710   int i, kill_x = -1, kill_y = -1;
13711
13712   int bad_element = -1;
13713   struct XY *test_xy = xy_topdown;
13714   static int test_dir[4] =
13715   {
13716     MV_UP,
13717     MV_LEFT,
13718     MV_RIGHT,
13719     MV_DOWN
13720   };
13721
13722   for (i = 0; i < NUM_DIRECTIONS; i++)
13723   {
13724     int test_x, test_y, test_move_dir, test_element;
13725
13726     test_x = good_x + test_xy[i].x;
13727     test_y = good_y + test_xy[i].y;
13728
13729     if (!IN_LEV_FIELD(test_x, test_y))
13730       continue;
13731
13732     test_move_dir =
13733       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13734
13735     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13736
13737     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13738        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13739     */
13740     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13741         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13742     {
13743       kill_x = test_x;
13744       kill_y = test_y;
13745       bad_element = test_element;
13746
13747       break;
13748     }
13749   }
13750
13751   if (kill_x != -1 || kill_y != -1)
13752   {
13753     if (IS_PLAYER(good_x, good_y))
13754     {
13755       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13756
13757       if (player->shield_deadly_time_left > 0 &&
13758           !IS_INDESTRUCTIBLE(bad_element))
13759         Bang(kill_x, kill_y);
13760       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13761         KillPlayer(player);
13762     }
13763     else
13764       Bang(good_x, good_y);
13765   }
13766 }
13767
13768 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13769 {
13770   int i, kill_x = -1, kill_y = -1;
13771   int bad_element = Tile[bad_x][bad_y];
13772   struct XY *test_xy = xy_topdown;
13773   static int touch_dir[4] =
13774   {
13775     MV_LEFT | MV_RIGHT,
13776     MV_UP   | MV_DOWN,
13777     MV_UP   | MV_DOWN,
13778     MV_LEFT | MV_RIGHT
13779   };
13780   static int test_dir[4] =
13781   {
13782     MV_UP,
13783     MV_LEFT,
13784     MV_RIGHT,
13785     MV_DOWN
13786   };
13787
13788   if (bad_element == EL_EXPLOSION)      // skip just exploding bad things
13789     return;
13790
13791   for (i = 0; i < NUM_DIRECTIONS; i++)
13792   {
13793     int test_x, test_y, test_move_dir, test_element;
13794
13795     test_x = bad_x + test_xy[i].x;
13796     test_y = bad_y + test_xy[i].y;
13797
13798     if (!IN_LEV_FIELD(test_x, test_y))
13799       continue;
13800
13801     test_move_dir =
13802       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13803
13804     test_element = Tile[test_x][test_y];
13805
13806     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13807        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13808     */
13809     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13810         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13811     {
13812       // good thing is player or penguin that does not move away
13813       if (IS_PLAYER(test_x, test_y))
13814       {
13815         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13816
13817         if (bad_element == EL_ROBOT && player->is_moving)
13818           continue;     // robot does not kill player if he is moving
13819
13820         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13821         {
13822           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13823             continue;           // center and border element do not touch
13824         }
13825
13826         kill_x = test_x;
13827         kill_y = test_y;
13828
13829         break;
13830       }
13831       else if (test_element == EL_PENGUIN)
13832       {
13833         kill_x = test_x;
13834         kill_y = test_y;
13835
13836         break;
13837       }
13838     }
13839   }
13840
13841   if (kill_x != -1 || kill_y != -1)
13842   {
13843     if (IS_PLAYER(kill_x, kill_y))
13844     {
13845       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13846
13847       if (player->shield_deadly_time_left > 0 &&
13848           !IS_INDESTRUCTIBLE(bad_element))
13849         Bang(bad_x, bad_y);
13850       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13851         KillPlayer(player);
13852     }
13853     else
13854       Bang(kill_x, kill_y);
13855   }
13856 }
13857
13858 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13859 {
13860   int bad_element = Tile[bad_x][bad_y];
13861   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13862   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13863   int test_x = bad_x + dx, test_y = bad_y + dy;
13864   int test_move_dir, test_element;
13865   int kill_x = -1, kill_y = -1;
13866
13867   if (!IN_LEV_FIELD(test_x, test_y))
13868     return;
13869
13870   test_move_dir =
13871     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13872
13873   test_element = Tile[test_x][test_y];
13874
13875   if (test_move_dir != bad_move_dir)
13876   {
13877     // good thing can be player or penguin that does not move away
13878     if (IS_PLAYER(test_x, test_y))
13879     {
13880       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13881
13882       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13883          player as being hit when he is moving towards the bad thing, because
13884          the "get hit by" condition would be lost after the player stops) */
13885       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13886         return;         // player moves away from bad thing
13887
13888       kill_x = test_x;
13889       kill_y = test_y;
13890     }
13891     else if (test_element == EL_PENGUIN)
13892     {
13893       kill_x = test_x;
13894       kill_y = test_y;
13895     }
13896   }
13897
13898   if (kill_x != -1 || kill_y != -1)
13899   {
13900     if (IS_PLAYER(kill_x, kill_y))
13901     {
13902       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13903
13904       if (player->shield_deadly_time_left > 0 &&
13905           !IS_INDESTRUCTIBLE(bad_element))
13906         Bang(bad_x, bad_y);
13907       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13908         KillPlayer(player);
13909     }
13910     else
13911       Bang(kill_x, kill_y);
13912   }
13913 }
13914
13915 void TestIfPlayerTouchesBadThing(int x, int y)
13916 {
13917   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13918 }
13919
13920 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13921 {
13922   TestIfGoodThingHitsBadThing(x, y, move_dir);
13923 }
13924
13925 void TestIfBadThingTouchesPlayer(int x, int y)
13926 {
13927   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13928 }
13929
13930 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13931 {
13932   TestIfBadThingHitsGoodThing(x, y, move_dir);
13933 }
13934
13935 void TestIfFriendTouchesBadThing(int x, int y)
13936 {
13937   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13938 }
13939
13940 void TestIfBadThingTouchesFriend(int x, int y)
13941 {
13942   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13943 }
13944
13945 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13946 {
13947   int i, kill_x = bad_x, kill_y = bad_y;
13948   struct XY *xy = xy_topdown;
13949
13950   for (i = 0; i < NUM_DIRECTIONS; i++)
13951   {
13952     int x, y, element;
13953
13954     x = bad_x + xy[i].x;
13955     y = bad_y + xy[i].y;
13956     if (!IN_LEV_FIELD(x, y))
13957       continue;
13958
13959     element = Tile[x][y];
13960     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13961         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13962     {
13963       kill_x = x;
13964       kill_y = y;
13965       break;
13966     }
13967   }
13968
13969   if (kill_x != bad_x || kill_y != bad_y)
13970     Bang(bad_x, bad_y);
13971 }
13972
13973 void KillPlayer(struct PlayerInfo *player)
13974 {
13975   int jx = player->jx, jy = player->jy;
13976
13977   if (!player->active)
13978     return;
13979
13980 #if 0
13981   Debug("game:playing:KillPlayer",
13982         "0: killed == %d, active == %d, reanimated == %d",
13983         player->killed, player->active, player->reanimated);
13984 #endif
13985
13986   /* the following code was introduced to prevent an infinite loop when calling
13987      -> Bang()
13988      -> CheckTriggeredElementChangeExt()
13989      -> ExecuteCustomElementAction()
13990      -> KillPlayer()
13991      -> (infinitely repeating the above sequence of function calls)
13992      which occurs when killing the player while having a CE with the setting
13993      "kill player X when explosion of <player X>"; the solution using a new
13994      field "player->killed" was chosen for backwards compatibility, although
13995      clever use of the fields "player->active" etc. would probably also work */
13996 #if 1
13997   if (player->killed)
13998     return;
13999 #endif
14000
14001   player->killed = TRUE;
14002
14003   // remove accessible field at the player's position
14004   RemoveField(jx, jy);
14005
14006   // deactivate shield (else Bang()/Explode() would not work right)
14007   player->shield_normal_time_left = 0;
14008   player->shield_deadly_time_left = 0;
14009
14010 #if 0
14011   Debug("game:playing:KillPlayer",
14012         "1: killed == %d, active == %d, reanimated == %d",
14013         player->killed, player->active, player->reanimated);
14014 #endif
14015
14016   Bang(jx, jy);
14017
14018 #if 0
14019   Debug("game:playing:KillPlayer",
14020         "2: killed == %d, active == %d, reanimated == %d",
14021         player->killed, player->active, player->reanimated);
14022 #endif
14023
14024   if (player->reanimated)       // killed player may have been reanimated
14025     player->killed = player->reanimated = FALSE;
14026   else
14027     BuryPlayer(player);
14028 }
14029
14030 static void KillPlayerUnlessEnemyProtected(int x, int y)
14031 {
14032   if (!PLAYER_ENEMY_PROTECTED(x, y))
14033     KillPlayer(PLAYERINFO(x, y));
14034 }
14035
14036 static void KillPlayerUnlessExplosionProtected(int x, int y)
14037 {
14038   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14039     KillPlayer(PLAYERINFO(x, y));
14040 }
14041
14042 void BuryPlayer(struct PlayerInfo *player)
14043 {
14044   int jx = player->jx, jy = player->jy;
14045
14046   if (!player->active)
14047     return;
14048
14049   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14050
14051   RemovePlayer(player);
14052
14053   player->buried = TRUE;
14054
14055   if (game.all_players_gone)
14056     game.GameOver = TRUE;
14057 }
14058
14059 void RemovePlayer(struct PlayerInfo *player)
14060 {
14061   int jx = player->jx, jy = player->jy;
14062   int i, found = FALSE;
14063
14064   player->present = FALSE;
14065   player->active = FALSE;
14066
14067   // required for some CE actions (even if the player is not active anymore)
14068   player->MovPos = 0;
14069
14070   if (!ExplodeField[jx][jy])
14071     StorePlayer[jx][jy] = 0;
14072
14073   if (player->is_moving)
14074     TEST_DrawLevelField(player->last_jx, player->last_jy);
14075
14076   for (i = 0; i < MAX_PLAYERS; i++)
14077     if (stored_player[i].active)
14078       found = TRUE;
14079
14080   if (!found)
14081   {
14082     game.all_players_gone = TRUE;
14083     game.GameOver = TRUE;
14084   }
14085
14086   game.exit_x = game.robot_wheel_x = jx;
14087   game.exit_y = game.robot_wheel_y = jy;
14088 }
14089
14090 void ExitPlayer(struct PlayerInfo *player)
14091 {
14092   DrawPlayer(player);   // needed here only to cleanup last field
14093   RemovePlayer(player);
14094
14095   if (game.players_still_needed > 0)
14096     game.players_still_needed--;
14097 }
14098
14099 static void SetFieldForSnapping(int x, int y, int element, int direction,
14100                                 int player_index_bit)
14101 {
14102   struct ElementInfo *ei = &element_info[element];
14103   int direction_bit = MV_DIR_TO_BIT(direction);
14104   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14105   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14106                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14107
14108   Tile[x][y] = EL_ELEMENT_SNAPPING;
14109   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14110   MovDir[x][y] = direction;
14111   Store[x][y] = element;
14112   Store2[x][y] = player_index_bit;
14113
14114   ResetGfxAnimation(x, y);
14115
14116   GfxElement[x][y] = element;
14117   GfxAction[x][y] = action;
14118   GfxDir[x][y] = direction;
14119   GfxFrame[x][y] = -1;
14120 }
14121
14122 static void TestFieldAfterSnapping(int x, int y, int element, int direction,
14123                                    int player_index_bit)
14124 {
14125   TestIfElementTouchesCustomElement(x, y);      // for empty space
14126
14127   if (level.finish_dig_collect)
14128   {
14129     int dig_side = MV_DIR_OPPOSITE(direction);
14130     int change_event = (IS_DIGGABLE(element) ? CE_PLAYER_DIGS_X :
14131                         CE_PLAYER_COLLECTS_X);
14132
14133     CheckTriggeredElementChangeByPlayer(x, y, element, change_event,
14134                                         player_index_bit, dig_side);
14135     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14136                                         player_index_bit, dig_side);
14137   }
14138 }
14139
14140 /*
14141   =============================================================================
14142   checkDiagonalPushing()
14143   -----------------------------------------------------------------------------
14144   check if diagonal input device direction results in pushing of object
14145   (by checking if the alternative direction is walkable, diggable, ...)
14146   =============================================================================
14147 */
14148
14149 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14150                                     int x, int y, int real_dx, int real_dy)
14151 {
14152   int jx, jy, dx, dy, xx, yy;
14153
14154   if (real_dx == 0 || real_dy == 0)     // no diagonal direction => push
14155     return TRUE;
14156
14157   // diagonal direction: check alternative direction
14158   jx = player->jx;
14159   jy = player->jy;
14160   dx = x - jx;
14161   dy = y - jy;
14162   xx = jx + (dx == 0 ? real_dx : 0);
14163   yy = jy + (dy == 0 ? real_dy : 0);
14164
14165   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Tile[xx][yy]));
14166 }
14167
14168 /*
14169   =============================================================================
14170   DigField()
14171   -----------------------------------------------------------------------------
14172   x, y:                 field next to player (non-diagonal) to try to dig to
14173   real_dx, real_dy:     direction as read from input device (can be diagonal)
14174   =============================================================================
14175 */
14176
14177 static int DigField(struct PlayerInfo *player,
14178                     int oldx, int oldy, int x, int y,
14179                     int real_dx, int real_dy, int mode)
14180 {
14181   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14182   boolean player_was_pushing = player->is_pushing;
14183   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14184   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14185   int jx = oldx, jy = oldy;
14186   int dx = x - jx, dy = y - jy;
14187   int nextx = x + dx, nexty = y + dy;
14188   int move_direction = (dx == -1 ? MV_LEFT  :
14189                         dx == +1 ? MV_RIGHT :
14190                         dy == -1 ? MV_UP    :
14191                         dy == +1 ? MV_DOWN  : MV_NONE);
14192   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14193   int dig_side = MV_DIR_OPPOSITE(move_direction);
14194   int old_element = Tile[jx][jy];
14195   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14196   int collect_count;
14197
14198   if (is_player)                // function can also be called by EL_PENGUIN
14199   {
14200     if (player->MovPos == 0)
14201     {
14202       player->is_digging = FALSE;
14203       player->is_collecting = FALSE;
14204     }
14205
14206     if (player->MovPos == 0)    // last pushing move finished
14207       player->is_pushing = FALSE;
14208
14209     if (mode == DF_NO_PUSH)     // player just stopped pushing
14210     {
14211       player->is_switching = FALSE;
14212       player->push_delay = -1;
14213
14214       return MP_NO_ACTION;
14215     }
14216   }
14217   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14218     old_element = Back[jx][jy];
14219
14220   // in case of element dropped at player position, check background
14221   else if (Back[jx][jy] != EL_EMPTY &&
14222            game.engine_version >= VERSION_IDENT(2,2,0,0))
14223     old_element = Back[jx][jy];
14224
14225   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14226     return MP_NO_ACTION;        // field has no opening in this direction
14227
14228   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element, opposite_direction))
14229     return MP_NO_ACTION;        // field has no opening in this direction
14230
14231   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14232   {
14233     SplashAcid(x, y);
14234
14235     Tile[jx][jy] = player->artwork_element;
14236     InitMovingField(jx, jy, MV_DOWN);
14237     Store[jx][jy] = EL_ACID;
14238     ContinueMoving(jx, jy);
14239     BuryPlayer(player);
14240
14241     return MP_DONT_RUN_INTO;
14242   }
14243
14244   if (player_can_move && DONT_RUN_INTO(element))
14245   {
14246     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14247
14248     return MP_DONT_RUN_INTO;
14249   }
14250
14251   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14252     return MP_NO_ACTION;
14253
14254   collect_count = element_info[element].collect_count_initial;
14255
14256   if (!is_player && !IS_COLLECTIBLE(element))   // penguin cannot collect it
14257     return MP_NO_ACTION;
14258
14259   if (game.engine_version < VERSION_IDENT(2,2,0,0))
14260     player_can_move = player_can_move_or_snap;
14261
14262   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14263       game.engine_version >= VERSION_IDENT(2,2,0,0))
14264   {
14265     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14266                                player->index_bit, dig_side);
14267     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14268                                         player->index_bit, dig_side);
14269
14270     if (element == EL_DC_LANDMINE)
14271       Bang(x, y);
14272
14273     if (Tile[x][y] != element)          // field changed by snapping
14274       return MP_ACTION;
14275
14276     return MP_NO_ACTION;
14277   }
14278
14279   if (player->gravity && is_player && !player->is_auto_moving &&
14280       canFallDown(player) && move_direction != MV_DOWN &&
14281       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14282     return MP_NO_ACTION;        // player cannot walk here due to gravity
14283
14284   if (player_can_move &&
14285       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14286   {
14287     int sound_element = SND_ELEMENT(element);
14288     int sound_action = ACTION_WALKING;
14289
14290     if (IS_RND_GATE(element))
14291     {
14292       if (!player->key[RND_GATE_NR(element)])
14293         return MP_NO_ACTION;
14294     }
14295     else if (IS_RND_GATE_GRAY(element))
14296     {
14297       if (!player->key[RND_GATE_GRAY_NR(element)])
14298         return MP_NO_ACTION;
14299     }
14300     else if (IS_RND_GATE_GRAY_ACTIVE(element))
14301     {
14302       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14303         return MP_NO_ACTION;
14304     }
14305     else if (element == EL_EXIT_OPEN ||
14306              element == EL_EM_EXIT_OPEN ||
14307              element == EL_EM_EXIT_OPENING ||
14308              element == EL_STEEL_EXIT_OPEN ||
14309              element == EL_EM_STEEL_EXIT_OPEN ||
14310              element == EL_EM_STEEL_EXIT_OPENING ||
14311              element == EL_SP_EXIT_OPEN ||
14312              element == EL_SP_EXIT_OPENING)
14313     {
14314       sound_action = ACTION_PASSING;    // player is passing exit
14315     }
14316     else if (element == EL_EMPTY)
14317     {
14318       sound_action = ACTION_MOVING;             // nothing to walk on
14319     }
14320
14321     // play sound from background or player, whatever is available
14322     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14323       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14324     else
14325       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14326   }
14327   else if (player_can_move &&
14328            IS_PASSABLE(element) && canPassField(x, y, move_direction))
14329   {
14330     if (!ACCESS_FROM(element, opposite_direction))
14331       return MP_NO_ACTION;      // field not accessible from this direction
14332
14333     if (CAN_MOVE(element))      // only fixed elements can be passed!
14334       return MP_NO_ACTION;
14335
14336     if (IS_EM_GATE(element))
14337     {
14338       if (!player->key[EM_GATE_NR(element)])
14339         return MP_NO_ACTION;
14340     }
14341     else if (IS_EM_GATE_GRAY(element))
14342     {
14343       if (!player->key[EM_GATE_GRAY_NR(element)])
14344         return MP_NO_ACTION;
14345     }
14346     else if (IS_EM_GATE_GRAY_ACTIVE(element))
14347     {
14348       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14349         return MP_NO_ACTION;
14350     }
14351     else if (IS_EMC_GATE(element))
14352     {
14353       if (!player->key[EMC_GATE_NR(element)])
14354         return MP_NO_ACTION;
14355     }
14356     else if (IS_EMC_GATE_GRAY(element))
14357     {
14358       if (!player->key[EMC_GATE_GRAY_NR(element)])
14359         return MP_NO_ACTION;
14360     }
14361     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14362     {
14363       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14364         return MP_NO_ACTION;
14365     }
14366     else if (element == EL_DC_GATE_WHITE ||
14367              element == EL_DC_GATE_WHITE_GRAY ||
14368              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14369     {
14370       if (player->num_white_keys == 0)
14371         return MP_NO_ACTION;
14372
14373       player->num_white_keys--;
14374     }
14375     else if (IS_SP_PORT(element))
14376     {
14377       if (element == EL_SP_GRAVITY_PORT_LEFT ||
14378           element == EL_SP_GRAVITY_PORT_RIGHT ||
14379           element == EL_SP_GRAVITY_PORT_UP ||
14380           element == EL_SP_GRAVITY_PORT_DOWN)
14381         player->gravity = !player->gravity;
14382       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14383                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14384                element == EL_SP_GRAVITY_ON_PORT_UP ||
14385                element == EL_SP_GRAVITY_ON_PORT_DOWN)
14386         player->gravity = TRUE;
14387       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14388                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14389                element == EL_SP_GRAVITY_OFF_PORT_UP ||
14390                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14391         player->gravity = FALSE;
14392     }
14393
14394     // automatically move to the next field with double speed
14395     player->programmed_action = move_direction;
14396
14397     if (player->move_delay_reset_counter == 0)
14398     {
14399       player->move_delay_reset_counter = 2;     // two double speed steps
14400
14401       DOUBLE_PLAYER_SPEED(player);
14402     }
14403
14404     PlayLevelSoundAction(x, y, ACTION_PASSING);
14405   }
14406   else if (player_can_move_or_snap && IS_DIGGABLE(element))
14407   {
14408     RemoveField(x, y);
14409
14410     if (mode != DF_SNAP)
14411     {
14412       GfxElement[x][y] = GFX_ELEMENT(element);
14413       player->is_digging = TRUE;
14414     }
14415
14416     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14417
14418     // use old behaviour for old levels (digging)
14419     if (!level.finish_dig_collect)
14420     {
14421       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14422                                           player->index_bit, dig_side);
14423
14424       // if digging triggered player relocation, finish digging tile
14425       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14426         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14427     }
14428
14429     if (mode == DF_SNAP)
14430     {
14431       if (level.block_snap_field)
14432         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14433       else
14434         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14435
14436       // use old behaviour for old levels (snapping)
14437       if (!level.finish_dig_collect)
14438         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14439                                             player->index_bit, dig_side);
14440     }
14441   }
14442   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14443   {
14444     RemoveField(x, y);
14445
14446     if (is_player && mode != DF_SNAP)
14447     {
14448       GfxElement[x][y] = element;
14449       player->is_collecting = TRUE;
14450     }
14451
14452     if (element == EL_SPEED_PILL)
14453     {
14454       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14455     }
14456     else if (element == EL_EXTRA_TIME && level.time > 0)
14457     {
14458       TimeLeft += level.extra_time;
14459
14460       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14461
14462       DisplayGameControlValues();
14463     }
14464     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14465     {
14466       int shield_time = (element == EL_SHIELD_DEADLY ?
14467                          level.shield_deadly_time :
14468                          level.shield_normal_time);
14469
14470       player->shield_normal_time_left += shield_time;
14471       if (element == EL_SHIELD_DEADLY)
14472         player->shield_deadly_time_left += shield_time;
14473     }
14474     else if (element == EL_DYNAMITE ||
14475              element == EL_EM_DYNAMITE ||
14476              element == EL_SP_DISK_RED)
14477     {
14478       if (player->inventory_size < MAX_INVENTORY_SIZE)
14479         player->inventory_element[player->inventory_size++] = element;
14480
14481       DrawGameDoorValues();
14482     }
14483     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14484     {
14485       player->dynabomb_count++;
14486       player->dynabombs_left++;
14487     }
14488     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14489     {
14490       player->dynabomb_size++;
14491     }
14492     else if (element == EL_DYNABOMB_INCREASE_POWER)
14493     {
14494       player->dynabomb_xl = TRUE;
14495     }
14496     else if (IS_KEY(element))
14497     {
14498       player->key[KEY_NR(element)] = TRUE;
14499
14500       DrawGameDoorValues();
14501     }
14502     else if (element == EL_DC_KEY_WHITE)
14503     {
14504       player->num_white_keys++;
14505
14506       // display white keys?
14507       // DrawGameDoorValues();
14508     }
14509     else if (IS_ENVELOPE(element))
14510     {
14511       boolean wait_for_snapping = (mode == DF_SNAP && level.block_snap_field);
14512
14513       if (!wait_for_snapping)
14514         player->show_envelope = element;
14515     }
14516     else if (element == EL_EMC_LENSES)
14517     {
14518       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14519
14520       RedrawAllInvisibleElementsForLenses();
14521     }
14522     else if (element == EL_EMC_MAGNIFIER)
14523     {
14524       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14525
14526       RedrawAllInvisibleElementsForMagnifier();
14527     }
14528     else if (IS_DROPPABLE(element) ||
14529              IS_THROWABLE(element))     // can be collected and dropped
14530     {
14531       int i;
14532
14533       if (collect_count == 0)
14534         player->inventory_infinite_element = element;
14535       else
14536         for (i = 0; i < collect_count; i++)
14537           if (player->inventory_size < MAX_INVENTORY_SIZE)
14538             player->inventory_element[player->inventory_size++] = element;
14539
14540       DrawGameDoorValues();
14541     }
14542     else if (collect_count > 0)
14543     {
14544       game.gems_still_needed -= collect_count;
14545       if (game.gems_still_needed < 0)
14546         game.gems_still_needed = 0;
14547
14548       game.snapshot.collected_item = TRUE;
14549
14550       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
14551
14552       DisplayGameControlValues();
14553     }
14554
14555     RaiseScoreElement(element);
14556     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14557
14558     // use old behaviour for old levels (collecting)
14559     if (!level.finish_dig_collect && is_player)
14560     {
14561       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14562                                           player->index_bit, dig_side);
14563
14564       // if collecting triggered player relocation, finish collecting tile
14565       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14566         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14567     }
14568
14569     if (mode == DF_SNAP)
14570     {
14571       if (level.block_snap_field)
14572         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14573       else
14574         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14575
14576       // use old behaviour for old levels (snapping)
14577       if (!level.finish_dig_collect)
14578         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14579                                             player->index_bit, dig_side);
14580     }
14581   }
14582   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14583   {
14584     if (mode == DF_SNAP && element != EL_BD_ROCK)
14585       return MP_NO_ACTION;
14586
14587     if (CAN_FALL(element) && dy)
14588       return MP_NO_ACTION;
14589
14590     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14591         !(element == EL_SPRING && level.use_spring_bug))
14592       return MP_NO_ACTION;
14593
14594     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14595         ((move_direction & MV_VERTICAL &&
14596           ((element_info[element].move_pattern & MV_LEFT &&
14597             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14598            (element_info[element].move_pattern & MV_RIGHT &&
14599             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14600          (move_direction & MV_HORIZONTAL &&
14601           ((element_info[element].move_pattern & MV_UP &&
14602             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14603            (element_info[element].move_pattern & MV_DOWN &&
14604             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14605       return MP_NO_ACTION;
14606
14607     // do not push elements already moving away faster than player
14608     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14609         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14610       return MP_NO_ACTION;
14611
14612     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14613     {
14614       if (player->push_delay_value == -1 || !player_was_pushing)
14615         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14616     }
14617     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14618     {
14619       if (player->push_delay_value == -1)
14620         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14621     }
14622     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14623     {
14624       if (!player->is_pushing)
14625         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14626     }
14627
14628     player->is_pushing = TRUE;
14629     player->is_active = TRUE;
14630
14631     if (!(IN_LEV_FIELD(nextx, nexty) &&
14632           (IS_FREE(nextx, nexty) ||
14633            (IS_SB_ELEMENT(element) &&
14634             Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14635            (IS_CUSTOM_ELEMENT(element) &&
14636             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
14637       return MP_NO_ACTION;
14638
14639     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14640       return MP_NO_ACTION;
14641
14642     if (player->push_delay == -1)       // new pushing; restart delay
14643       player->push_delay = 0;
14644
14645     if (player->push_delay < player->push_delay_value &&
14646         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14647         element != EL_SPRING && element != EL_BALLOON)
14648     {
14649       // make sure that there is no move delay before next try to push
14650       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14651         player->move_delay = 0;
14652
14653       return MP_NO_ACTION;
14654     }
14655
14656     if (IS_CUSTOM_ELEMENT(element) &&
14657         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14658     {
14659       if (!DigFieldByCE(nextx, nexty, element))
14660         return MP_NO_ACTION;
14661     }
14662
14663     if (IS_SB_ELEMENT(element))
14664     {
14665       boolean sokoban_task_solved = FALSE;
14666
14667       if (element == EL_SOKOBAN_FIELD_FULL)
14668       {
14669         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14670
14671         IncrementSokobanFieldsNeeded();
14672         IncrementSokobanObjectsNeeded();
14673       }
14674
14675       if (Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14676       {
14677         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14678
14679         DecrementSokobanFieldsNeeded();
14680         DecrementSokobanObjectsNeeded();
14681
14682         // sokoban object was pushed from empty field to sokoban field
14683         if (Back[x][y] == EL_EMPTY)
14684           sokoban_task_solved = TRUE;
14685       }
14686
14687       Tile[x][y] = EL_SOKOBAN_OBJECT;
14688
14689       if (Back[x][y] == Back[nextx][nexty])
14690         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14691       else if (Back[x][y] != 0)
14692         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14693                                     ACTION_EMPTYING);
14694       else
14695         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14696                                     ACTION_FILLING);
14697
14698       if (sokoban_task_solved &&
14699           game.sokoban_fields_still_needed == 0 &&
14700           game.sokoban_objects_still_needed == 0 &&
14701           level.auto_exit_sokoban)
14702       {
14703         game.players_still_needed = 0;
14704
14705         LevelSolved();
14706
14707         PlaySound(SND_GAME_SOKOBAN_SOLVING);
14708       }
14709     }
14710     else
14711       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14712
14713     InitMovingField(x, y, move_direction);
14714     GfxAction[x][y] = ACTION_PUSHING;
14715
14716     if (mode == DF_SNAP)
14717       ContinueMoving(x, y);
14718     else
14719       MovPos[x][y] = (dx != 0 ? dx : dy);
14720
14721     Pushed[x][y] = TRUE;
14722     Pushed[nextx][nexty] = TRUE;
14723
14724     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14725       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14726     else
14727       player->push_delay_value = -1;    // get new value later
14728
14729     // check for element change _after_ element has been pushed
14730     if (game.use_change_when_pushing_bug)
14731     {
14732       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14733                                  player->index_bit, dig_side);
14734       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14735                                           player->index_bit, dig_side);
14736     }
14737   }
14738   else if (IS_SWITCHABLE(element))
14739   {
14740     if (PLAYER_SWITCHING(player, x, y))
14741     {
14742       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14743                                           player->index_bit, dig_side);
14744
14745       return MP_ACTION;
14746     }
14747
14748     player->is_switching = TRUE;
14749     player->switch_x = x;
14750     player->switch_y = y;
14751
14752     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14753
14754     if (element == EL_ROBOT_WHEEL)
14755     {
14756       Tile[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14757
14758       game.robot_wheel_x = x;
14759       game.robot_wheel_y = y;
14760       game.robot_wheel_active = TRUE;
14761
14762       TEST_DrawLevelField(x, y);
14763     }
14764     else if (element == EL_SP_TERMINAL)
14765     {
14766       int xx, yy;
14767
14768       SCAN_PLAYFIELD(xx, yy)
14769       {
14770         if (Tile[xx][yy] == EL_SP_DISK_YELLOW)
14771         {
14772           Bang(xx, yy);
14773         }
14774         else if (Tile[xx][yy] == EL_SP_TERMINAL)
14775         {
14776           Tile[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14777
14778           ResetGfxAnimation(xx, yy);
14779           TEST_DrawLevelField(xx, yy);
14780         }
14781       }
14782     }
14783     else if (IS_BELT_SWITCH(element))
14784     {
14785       ToggleBeltSwitch(x, y);
14786     }
14787     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14788              element == EL_SWITCHGATE_SWITCH_DOWN ||
14789              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14790              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14791     {
14792       ToggleSwitchgateSwitch();
14793     }
14794     else if (element == EL_LIGHT_SWITCH ||
14795              element == EL_LIGHT_SWITCH_ACTIVE)
14796     {
14797       ToggleLightSwitch(x, y);
14798     }
14799     else if (element == EL_TIMEGATE_SWITCH ||
14800              element == EL_DC_TIMEGATE_SWITCH)
14801     {
14802       ActivateTimegateSwitch(x, y);
14803     }
14804     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14805              element == EL_BALLOON_SWITCH_RIGHT ||
14806              element == EL_BALLOON_SWITCH_UP    ||
14807              element == EL_BALLOON_SWITCH_DOWN  ||
14808              element == EL_BALLOON_SWITCH_NONE  ||
14809              element == EL_BALLOON_SWITCH_ANY)
14810     {
14811       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14812                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14813                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14814                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14815                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14816                              move_direction);
14817     }
14818     else if (element == EL_LAMP)
14819     {
14820       Tile[x][y] = EL_LAMP_ACTIVE;
14821       game.lights_still_needed--;
14822
14823       ResetGfxAnimation(x, y);
14824       TEST_DrawLevelField(x, y);
14825     }
14826     else if (element == EL_TIME_ORB_FULL)
14827     {
14828       Tile[x][y] = EL_TIME_ORB_EMPTY;
14829
14830       if (level.time > 0 || level.use_time_orb_bug)
14831       {
14832         TimeLeft += level.time_orb_time;
14833         game.no_level_time_limit = FALSE;
14834
14835         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14836
14837         DisplayGameControlValues();
14838       }
14839
14840       ResetGfxAnimation(x, y);
14841       TEST_DrawLevelField(x, y);
14842     }
14843     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14844              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14845     {
14846       int xx, yy;
14847
14848       game.ball_active = !game.ball_active;
14849
14850       SCAN_PLAYFIELD(xx, yy)
14851       {
14852         int e = Tile[xx][yy];
14853
14854         if (game.ball_active)
14855         {
14856           if (e == EL_EMC_MAGIC_BALL)
14857             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14858           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14859             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14860         }
14861         else
14862         {
14863           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14864             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14865           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14866             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14867         }
14868       }
14869     }
14870
14871     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14872                                         player->index_bit, dig_side);
14873
14874     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14875                                         player->index_bit, dig_side);
14876
14877     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14878                                         player->index_bit, dig_side);
14879
14880     return MP_ACTION;
14881   }
14882   else
14883   {
14884     if (!PLAYER_SWITCHING(player, x, y))
14885     {
14886       player->is_switching = TRUE;
14887       player->switch_x = x;
14888       player->switch_y = y;
14889
14890       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14891                                  player->index_bit, dig_side);
14892       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14893                                           player->index_bit, dig_side);
14894
14895       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14896                                  player->index_bit, dig_side);
14897       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14898                                           player->index_bit, dig_side);
14899     }
14900
14901     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14902                                player->index_bit, dig_side);
14903     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14904                                         player->index_bit, dig_side);
14905
14906     return MP_NO_ACTION;
14907   }
14908
14909   player->push_delay = -1;
14910
14911   if (is_player)                // function can also be called by EL_PENGUIN
14912   {
14913     if (Tile[x][y] != element)          // really digged/collected something
14914     {
14915       player->is_collecting = !player->is_digging;
14916       player->is_active = TRUE;
14917
14918       player->last_removed_element = element;
14919     }
14920   }
14921
14922   return MP_MOVING;
14923 }
14924
14925 static boolean DigFieldByCE(int x, int y, int digging_element)
14926 {
14927   int element = Tile[x][y];
14928
14929   if (!IS_FREE(x, y))
14930   {
14931     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14932                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14933                   ACTION_BREAKING);
14934
14935     // no element can dig solid indestructible elements
14936     if (IS_INDESTRUCTIBLE(element) &&
14937         !IS_DIGGABLE(element) &&
14938         !IS_COLLECTIBLE(element))
14939       return FALSE;
14940
14941     if (AmoebaNr[x][y] &&
14942         (element == EL_AMOEBA_FULL ||
14943          element == EL_BD_AMOEBA ||
14944          element == EL_AMOEBA_GROWING))
14945     {
14946       AmoebaCnt[AmoebaNr[x][y]]--;
14947       AmoebaCnt2[AmoebaNr[x][y]]--;
14948     }
14949
14950     if (IS_MOVING(x, y))
14951       RemoveMovingField(x, y);
14952     else
14953     {
14954       RemoveField(x, y);
14955       TEST_DrawLevelField(x, y);
14956     }
14957
14958     // if digged element was about to explode, prevent the explosion
14959     ExplodeField[x][y] = EX_TYPE_NONE;
14960
14961     PlayLevelSoundAction(x, y, action);
14962   }
14963
14964   Store[x][y] = EL_EMPTY;
14965
14966   // this makes it possible to leave the removed element again
14967   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14968     Store[x][y] = element;
14969
14970   return TRUE;
14971 }
14972
14973 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14974 {
14975   int jx = player->jx, jy = player->jy;
14976   int x = jx + dx, y = jy + dy;
14977   int snap_direction = (dx == -1 ? MV_LEFT  :
14978                         dx == +1 ? MV_RIGHT :
14979                         dy == -1 ? MV_UP    :
14980                         dy == +1 ? MV_DOWN  : MV_NONE);
14981   boolean can_continue_snapping = (level.continuous_snapping &&
14982                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14983
14984   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14985     return FALSE;
14986
14987   if (!player->active || !IN_LEV_FIELD(x, y))
14988     return FALSE;
14989
14990   if (dx && dy)
14991     return FALSE;
14992
14993   if (!dx && !dy)
14994   {
14995     if (player->MovPos == 0)
14996       player->is_pushing = FALSE;
14997
14998     player->is_snapping = FALSE;
14999
15000     if (player->MovPos == 0)
15001     {
15002       player->is_moving = FALSE;
15003       player->is_digging = FALSE;
15004       player->is_collecting = FALSE;
15005     }
15006
15007     return FALSE;
15008   }
15009
15010   // prevent snapping with already pressed snap key when not allowed
15011   if (player->is_snapping && !can_continue_snapping)
15012     return FALSE;
15013
15014   player->MovDir = snap_direction;
15015
15016   if (player->MovPos == 0)
15017   {
15018     player->is_moving = FALSE;
15019     player->is_digging = FALSE;
15020     player->is_collecting = FALSE;
15021   }
15022
15023   player->is_dropping = FALSE;
15024   player->is_dropping_pressed = FALSE;
15025   player->drop_pressed_delay = 0;
15026
15027   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
15028     return FALSE;
15029
15030   player->is_snapping = TRUE;
15031   player->is_active = TRUE;
15032
15033   if (player->MovPos == 0)
15034   {
15035     player->is_moving = FALSE;
15036     player->is_digging = FALSE;
15037     player->is_collecting = FALSE;
15038   }
15039
15040   if (player->MovPos != 0)      // prevent graphic bugs in versions < 2.2.0
15041     TEST_DrawLevelField(player->last_jx, player->last_jy);
15042
15043   TEST_DrawLevelField(x, y);
15044
15045   return TRUE;
15046 }
15047
15048 static boolean DropElement(struct PlayerInfo *player)
15049 {
15050   int old_element, new_element;
15051   int dropx = player->jx, dropy = player->jy;
15052   int drop_direction = player->MovDir;
15053   int drop_side = drop_direction;
15054   int drop_element = get_next_dropped_element(player);
15055
15056   /* do not drop an element on top of another element; when holding drop key
15057      pressed without moving, dropped element must move away before the next
15058      element can be dropped (this is especially important if the next element
15059      is dynamite, which can be placed on background for historical reasons) */
15060   if (PLAYER_DROPPING(player, dropx, dropy) && Tile[dropx][dropy] != EL_EMPTY)
15061     return MP_ACTION;
15062
15063   if (IS_THROWABLE(drop_element))
15064   {
15065     dropx += GET_DX_FROM_DIR(drop_direction);
15066     dropy += GET_DY_FROM_DIR(drop_direction);
15067
15068     if (!IN_LEV_FIELD(dropx, dropy))
15069       return FALSE;
15070   }
15071
15072   old_element = Tile[dropx][dropy];     // old element at dropping position
15073   new_element = drop_element;           // default: no change when dropping
15074
15075   // check if player is active, not moving and ready to drop
15076   if (!player->active || player->MovPos || player->drop_delay > 0)
15077     return FALSE;
15078
15079   // check if player has anything that can be dropped
15080   if (new_element == EL_UNDEFINED)
15081     return FALSE;
15082
15083   // only set if player has anything that can be dropped
15084   player->is_dropping_pressed = TRUE;
15085
15086   // check if drop key was pressed long enough for EM style dynamite
15087   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15088     return FALSE;
15089
15090   // check if anything can be dropped at the current position
15091   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15092     return FALSE;
15093
15094   // collected custom elements can only be dropped on empty fields
15095   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15096     return FALSE;
15097
15098   if (old_element != EL_EMPTY)
15099     Back[dropx][dropy] = old_element;   // store old element on this field
15100
15101   ResetGfxAnimation(dropx, dropy);
15102   ResetRandomAnimationValue(dropx, dropy);
15103
15104   if (player->inventory_size > 0 ||
15105       player->inventory_infinite_element != EL_UNDEFINED)
15106   {
15107     if (player->inventory_size > 0)
15108     {
15109       player->inventory_size--;
15110
15111       DrawGameDoorValues();
15112
15113       if (new_element == EL_DYNAMITE)
15114         new_element = EL_DYNAMITE_ACTIVE;
15115       else if (new_element == EL_EM_DYNAMITE)
15116         new_element = EL_EM_DYNAMITE_ACTIVE;
15117       else if (new_element == EL_SP_DISK_RED)
15118         new_element = EL_SP_DISK_RED_ACTIVE;
15119     }
15120
15121     Tile[dropx][dropy] = new_element;
15122
15123     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15124       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15125                           el2img(Tile[dropx][dropy]), 0);
15126
15127     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15128
15129     // needed if previous element just changed to "empty" in the last frame
15130     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
15131
15132     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15133                                player->index_bit, drop_side);
15134     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15135                                         CE_PLAYER_DROPS_X,
15136                                         player->index_bit, drop_side);
15137
15138     TestIfElementTouchesCustomElement(dropx, dropy);
15139   }
15140   else          // player is dropping a dyna bomb
15141   {
15142     player->dynabombs_left--;
15143
15144     Tile[dropx][dropy] = new_element;
15145
15146     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15147       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15148                           el2img(Tile[dropx][dropy]), 0);
15149
15150     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15151   }
15152
15153   if (Tile[dropx][dropy] == new_element) // uninitialized unless CE change
15154     InitField_WithBug1(dropx, dropy, FALSE);
15155
15156   new_element = Tile[dropx][dropy];     // element might have changed
15157
15158   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
15159       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
15160   {
15161     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
15162       MovDir[dropx][dropy] = drop_direction;
15163
15164     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
15165
15166     // do not cause impact style collision by dropping elements that can fall
15167     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15168   }
15169
15170   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
15171   player->is_dropping = TRUE;
15172
15173   player->drop_pressed_delay = 0;
15174   player->is_dropping_pressed = FALSE;
15175
15176   player->drop_x = dropx;
15177   player->drop_y = dropy;
15178
15179   return TRUE;
15180 }
15181
15182 // ----------------------------------------------------------------------------
15183 // game sound playing functions
15184 // ----------------------------------------------------------------------------
15185
15186 static int *loop_sound_frame = NULL;
15187 static int *loop_sound_volume = NULL;
15188
15189 void InitPlayLevelSound(void)
15190 {
15191   int num_sounds = getSoundListSize();
15192
15193   checked_free(loop_sound_frame);
15194   checked_free(loop_sound_volume);
15195
15196   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
15197   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
15198 }
15199
15200 static void PlayLevelSound(int x, int y, int nr)
15201 {
15202   int sx = SCREENX(x), sy = SCREENY(y);
15203   int volume, stereo_position;
15204   int max_distance = 8;
15205   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
15206
15207   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
15208       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
15209     return;
15210
15211   if (!IN_LEV_FIELD(x, y) ||
15212       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
15213       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
15214     return;
15215
15216   volume = SOUND_MAX_VOLUME;
15217
15218   if (!IN_SCR_FIELD(sx, sy))
15219   {
15220     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
15221     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
15222
15223     volume -= volume * (dx > dy ? dx : dy) / max_distance;
15224   }
15225
15226   stereo_position = (SOUND_MAX_LEFT +
15227                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
15228                      (SCR_FIELDX + 2 * max_distance));
15229
15230   if (IS_LOOP_SOUND(nr))
15231   {
15232     /* This assures that quieter loop sounds do not overwrite louder ones,
15233        while restarting sound volume comparison with each new game frame. */
15234
15235     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
15236       return;
15237
15238     loop_sound_volume[nr] = volume;
15239     loop_sound_frame[nr] = FrameCounter;
15240   }
15241
15242   PlaySoundExt(nr, volume, stereo_position, type);
15243 }
15244
15245 static void PlayLevelSoundNearest(int x, int y, int sound_action)
15246 {
15247   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
15248                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
15249                  y < LEVELY(BY1) ? LEVELY(BY1) :
15250                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
15251                  sound_action);
15252 }
15253
15254 static void PlayLevelSoundAction(int x, int y, int action)
15255 {
15256   PlayLevelSoundElementAction(x, y, Tile[x][y], action);
15257 }
15258
15259 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
15260 {
15261   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15262
15263   if (sound_effect != SND_UNDEFINED)
15264     PlayLevelSound(x, y, sound_effect);
15265 }
15266
15267 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
15268                                               int action)
15269 {
15270   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15271
15272   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15273     PlayLevelSound(x, y, sound_effect);
15274 }
15275
15276 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
15277 {
15278   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15279
15280   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15281     PlayLevelSound(x, y, sound_effect);
15282 }
15283
15284 static void StopLevelSoundActionIfLoop(int x, int y, int action)
15285 {
15286   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15287
15288   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15289     StopSound(sound_effect);
15290 }
15291
15292 static int getLevelMusicNr(void)
15293 {
15294   int level_pos = level_nr - leveldir_current->first_level;
15295
15296   if (levelset.music[level_nr] != MUS_UNDEFINED)
15297     return levelset.music[level_nr];            // from config file
15298   else
15299     return MAP_NOCONF_MUSIC(level_pos);         // from music dir
15300 }
15301
15302 static void FadeLevelSounds(void)
15303 {
15304   FadeSounds();
15305 }
15306
15307 static void FadeLevelMusic(void)
15308 {
15309   int music_nr = getLevelMusicNr();
15310   char *curr_music = getCurrentlyPlayingMusicFilename();
15311   char *next_music = getMusicInfoEntryFilename(music_nr);
15312
15313   if (!strEqual(curr_music, next_music))
15314     FadeMusic();
15315 }
15316
15317 void FadeLevelSoundsAndMusic(void)
15318 {
15319   FadeLevelSounds();
15320   FadeLevelMusic();
15321 }
15322
15323 static void PlayLevelMusic(void)
15324 {
15325   int music_nr = getLevelMusicNr();
15326   char *curr_music = getCurrentlyPlayingMusicFilename();
15327   char *next_music = getMusicInfoEntryFilename(music_nr);
15328
15329   if (!strEqual(curr_music, next_music))
15330     PlayMusicLoop(music_nr);
15331 }
15332
15333 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15334 {
15335   int element = (element_em > -1 ? map_element_EM_to_RND_game(element_em) : 0);
15336   int offset = 0;
15337   int x = xx - offset;
15338   int y = yy - offset;
15339
15340   switch (sample)
15341   {
15342     case SOUND_blank:
15343       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15344       break;
15345
15346     case SOUND_roll:
15347       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15348       break;
15349
15350     case SOUND_stone:
15351       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15352       break;
15353
15354     case SOUND_nut:
15355       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15356       break;
15357
15358     case SOUND_crack:
15359       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15360       break;
15361
15362     case SOUND_bug:
15363       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15364       break;
15365
15366     case SOUND_tank:
15367       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15368       break;
15369
15370     case SOUND_android_clone:
15371       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15372       break;
15373
15374     case SOUND_android_move:
15375       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15376       break;
15377
15378     case SOUND_spring:
15379       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15380       break;
15381
15382     case SOUND_slurp:
15383       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15384       break;
15385
15386     case SOUND_eater:
15387       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15388       break;
15389
15390     case SOUND_eater_eat:
15391       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15392       break;
15393
15394     case SOUND_alien:
15395       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15396       break;
15397
15398     case SOUND_collect:
15399       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15400       break;
15401
15402     case SOUND_diamond:
15403       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15404       break;
15405
15406     case SOUND_squash:
15407       // !!! CHECK THIS !!!
15408 #if 1
15409       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15410 #else
15411       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15412 #endif
15413       break;
15414
15415     case SOUND_wonderfall:
15416       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15417       break;
15418
15419     case SOUND_drip:
15420       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15421       break;
15422
15423     case SOUND_push:
15424       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15425       break;
15426
15427     case SOUND_dirt:
15428       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15429       break;
15430
15431     case SOUND_acid:
15432       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15433       break;
15434
15435     case SOUND_ball:
15436       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15437       break;
15438
15439     case SOUND_slide:
15440       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15441       break;
15442
15443     case SOUND_wonder:
15444       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15445       break;
15446
15447     case SOUND_door:
15448       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15449       break;
15450
15451     case SOUND_exit_open:
15452       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15453       break;
15454
15455     case SOUND_exit_leave:
15456       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15457       break;
15458
15459     case SOUND_dynamite:
15460       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15461       break;
15462
15463     case SOUND_tick:
15464       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15465       break;
15466
15467     case SOUND_press:
15468       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15469       break;
15470
15471     case SOUND_wheel:
15472       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15473       break;
15474
15475     case SOUND_boom:
15476       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15477       break;
15478
15479     case SOUND_die:
15480       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15481       break;
15482
15483     case SOUND_time:
15484       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15485       break;
15486
15487     default:
15488       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15489       break;
15490   }
15491 }
15492
15493 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
15494 {
15495   int element = map_element_SP_to_RND(element_sp);
15496   int action = map_action_SP_to_RND(action_sp);
15497   int offset = (setup.sp_show_border_elements ? 0 : 1);
15498   int x = xx - offset;
15499   int y = yy - offset;
15500
15501   PlayLevelSoundElementAction(x, y, element, action);
15502 }
15503
15504 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
15505 {
15506   int element = map_element_MM_to_RND(element_mm);
15507   int action = map_action_MM_to_RND(action_mm);
15508   int offset = 0;
15509   int x = xx - offset;
15510   int y = yy - offset;
15511
15512   if (!IS_MM_ELEMENT(element))
15513     element = EL_MM_DEFAULT;
15514
15515   PlayLevelSoundElementAction(x, y, element, action);
15516 }
15517
15518 void PlaySound_MM(int sound_mm)
15519 {
15520   int sound = map_sound_MM_to_RND(sound_mm);
15521
15522   if (sound == SND_UNDEFINED)
15523     return;
15524
15525   PlaySound(sound);
15526 }
15527
15528 void PlaySoundLoop_MM(int sound_mm)
15529 {
15530   int sound = map_sound_MM_to_RND(sound_mm);
15531
15532   if (sound == SND_UNDEFINED)
15533     return;
15534
15535   PlaySoundLoop(sound);
15536 }
15537
15538 void StopSound_MM(int sound_mm)
15539 {
15540   int sound = map_sound_MM_to_RND(sound_mm);
15541
15542   if (sound == SND_UNDEFINED)
15543     return;
15544
15545   StopSound(sound);
15546 }
15547
15548 void RaiseScore(int value)
15549 {
15550   game.score += value;
15551
15552   game_panel_controls[GAME_PANEL_SCORE].value = game.score;
15553
15554   DisplayGameControlValues();
15555 }
15556
15557 void RaiseScoreElement(int element)
15558 {
15559   switch (element)
15560   {
15561     case EL_EMERALD:
15562     case EL_BD_DIAMOND:
15563     case EL_EMERALD_YELLOW:
15564     case EL_EMERALD_RED:
15565     case EL_EMERALD_PURPLE:
15566     case EL_SP_INFOTRON:
15567       RaiseScore(level.score[SC_EMERALD]);
15568       break;
15569     case EL_DIAMOND:
15570       RaiseScore(level.score[SC_DIAMOND]);
15571       break;
15572     case EL_CRYSTAL:
15573       RaiseScore(level.score[SC_CRYSTAL]);
15574       break;
15575     case EL_PEARL:
15576       RaiseScore(level.score[SC_PEARL]);
15577       break;
15578     case EL_BUG:
15579     case EL_BD_BUTTERFLY:
15580     case EL_SP_ELECTRON:
15581       RaiseScore(level.score[SC_BUG]);
15582       break;
15583     case EL_SPACESHIP:
15584     case EL_BD_FIREFLY:
15585     case EL_SP_SNIKSNAK:
15586       RaiseScore(level.score[SC_SPACESHIP]);
15587       break;
15588     case EL_YAMYAM:
15589     case EL_DARK_YAMYAM:
15590       RaiseScore(level.score[SC_YAMYAM]);
15591       break;
15592     case EL_ROBOT:
15593       RaiseScore(level.score[SC_ROBOT]);
15594       break;
15595     case EL_PACMAN:
15596       RaiseScore(level.score[SC_PACMAN]);
15597       break;
15598     case EL_NUT:
15599       RaiseScore(level.score[SC_NUT]);
15600       break;
15601     case EL_DYNAMITE:
15602     case EL_EM_DYNAMITE:
15603     case EL_SP_DISK_RED:
15604     case EL_DYNABOMB_INCREASE_NUMBER:
15605     case EL_DYNABOMB_INCREASE_SIZE:
15606     case EL_DYNABOMB_INCREASE_POWER:
15607       RaiseScore(level.score[SC_DYNAMITE]);
15608       break;
15609     case EL_SHIELD_NORMAL:
15610     case EL_SHIELD_DEADLY:
15611       RaiseScore(level.score[SC_SHIELD]);
15612       break;
15613     case EL_EXTRA_TIME:
15614       RaiseScore(level.extra_time_score);
15615       break;
15616     case EL_KEY_1:
15617     case EL_KEY_2:
15618     case EL_KEY_3:
15619     case EL_KEY_4:
15620     case EL_EM_KEY_1:
15621     case EL_EM_KEY_2:
15622     case EL_EM_KEY_3:
15623     case EL_EM_KEY_4:
15624     case EL_EMC_KEY_5:
15625     case EL_EMC_KEY_6:
15626     case EL_EMC_KEY_7:
15627     case EL_EMC_KEY_8:
15628     case EL_DC_KEY_WHITE:
15629       RaiseScore(level.score[SC_KEY]);
15630       break;
15631     default:
15632       RaiseScore(element_info[element].collect_score);
15633       break;
15634   }
15635 }
15636
15637 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15638 {
15639   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15640   {
15641     if (!quick_quit)
15642     {
15643       // prevent short reactivation of overlay buttons while closing door
15644       SetOverlayActive(FALSE);
15645       UnmapGameButtons();
15646
15647       // door may still be open due to skipped or envelope style request
15648       CloseDoor(score_info_tape_play ? DOOR_CLOSE_ALL : DOOR_CLOSE_1);
15649     }
15650
15651     if (network.enabled)
15652       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15653     else
15654     {
15655       if (quick_quit)
15656         FadeSkipNextFadeIn();
15657
15658       SetGameStatus(GAME_MODE_MAIN);
15659
15660       DrawMainMenu();
15661     }
15662   }
15663   else          // continue playing the game
15664   {
15665     if (tape.playing && tape.deactivate_display)
15666       TapeDeactivateDisplayOff(TRUE);
15667
15668     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15669
15670     if (tape.playing && tape.deactivate_display)
15671       TapeDeactivateDisplayOn();
15672   }
15673 }
15674
15675 void RequestQuitGame(boolean escape_key_pressed)
15676 {
15677   boolean ask_on_escape = (setup.ask_on_escape && setup.ask_on_quit_game);
15678   boolean quick_quit = ((escape_key_pressed && !ask_on_escape) ||
15679                         level_editor_test_game);
15680   boolean skip_request = (game.all_players_gone || !setup.ask_on_quit_game ||
15681                           quick_quit || score_info_tape_play);
15682
15683   RequestQuitGameExt(skip_request, quick_quit,
15684                      "Do you really want to quit the game?");
15685 }
15686
15687 static char *getRestartGameMessage(void)
15688 {
15689   boolean play_again = hasStartedNetworkGame();
15690   static char message[MAX_OUTPUT_LINESIZE];
15691   char *game_over_text = "Game over!";
15692   char *play_again_text = " Play it again?";
15693
15694   if (level.game_engine_type == GAME_ENGINE_TYPE_MM &&
15695       game_mm.game_over_message != NULL)
15696     game_over_text = game_mm.game_over_message;
15697
15698   snprintf(message, MAX_OUTPUT_LINESIZE, "%s%s", game_over_text,
15699            (play_again ? play_again_text : ""));
15700
15701   return message;
15702 }
15703
15704 static void RequestRestartGame(void)
15705 {
15706   char *message = getRestartGameMessage();
15707   boolean has_started_game = hasStartedNetworkGame();
15708   int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM);
15709   int door_state = DOOR_CLOSE_1;
15710
15711   if (Request(message, request_mode | REQ_STAY_OPEN) && has_started_game)
15712   {
15713     CloseDoor(door_state);
15714
15715     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15716   }
15717   else
15718   {
15719     // if game was invoked from level editor, also close tape recorder door
15720     if (level_editor_test_game)
15721       door_state = DOOR_CLOSE_ALL;
15722
15723     CloseDoor(door_state);
15724
15725     SetGameStatus(GAME_MODE_MAIN);
15726
15727     DrawMainMenu();
15728   }
15729 }
15730
15731 boolean CheckRestartGame(void)
15732 {
15733   static int game_over_delay = 0;
15734   int game_over_delay_value = 50;
15735   boolean game_over = checkGameFailed();
15736
15737   if (!game_over)
15738   {
15739     game_over_delay = game_over_delay_value;
15740
15741     return FALSE;
15742   }
15743
15744   if (game_over_delay > 0)
15745   {
15746     if (game_over_delay == game_over_delay_value / 2)
15747       PlaySound(SND_GAME_LOSING);
15748
15749     game_over_delay--;
15750
15751     return FALSE;
15752   }
15753
15754   // do not handle game over if request dialog is already active
15755   if (game.request_active)
15756     return FALSE;
15757
15758   // do not ask to play again if game was never actually played
15759   if (!game.GamePlayed)
15760     return FALSE;
15761
15762   // do not ask to play again if this was disabled in setup menu
15763   if (!setup.ask_on_game_over)
15764     return FALSE;
15765
15766   RequestRestartGame();
15767
15768   return TRUE;
15769 }
15770
15771 boolean checkGameSolved(void)
15772 {
15773   // set for all game engines if level was solved
15774   return game.LevelSolved_GameEnd;
15775 }
15776
15777 boolean checkGameFailed(void)
15778 {
15779   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15780     return (game_em.game_over && !game_em.level_solved);
15781   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15782     return (game_sp.game_over && !game_sp.level_solved);
15783   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15784     return (game_mm.game_over && !game_mm.level_solved);
15785   else                          // GAME_ENGINE_TYPE_RND
15786     return (game.GameOver && !game.LevelSolved);
15787 }
15788
15789 boolean checkGameEnded(void)
15790 {
15791   return (checkGameSolved() || checkGameFailed());
15792 }
15793
15794
15795 // ----------------------------------------------------------------------------
15796 // random generator functions
15797 // ----------------------------------------------------------------------------
15798
15799 unsigned int InitEngineRandom_RND(int seed)
15800 {
15801   game.num_random_calls = 0;
15802
15803   return InitEngineRandom(seed);
15804 }
15805
15806 unsigned int RND(int max)
15807 {
15808   if (max > 0)
15809   {
15810     game.num_random_calls++;
15811
15812     return GetEngineRandom(max);
15813   }
15814
15815   return 0;
15816 }
15817
15818
15819 // ----------------------------------------------------------------------------
15820 // game engine snapshot handling functions
15821 // ----------------------------------------------------------------------------
15822
15823 struct EngineSnapshotInfo
15824 {
15825   // runtime values for custom element collect score
15826   int collect_score[NUM_CUSTOM_ELEMENTS];
15827
15828   // runtime values for group element choice position
15829   int choice_pos[NUM_GROUP_ELEMENTS];
15830
15831   // runtime values for belt position animations
15832   int belt_graphic[4][NUM_BELT_PARTS];
15833   int belt_anim_mode[4][NUM_BELT_PARTS];
15834 };
15835
15836 static struct EngineSnapshotInfo engine_snapshot_rnd;
15837 static char *snapshot_level_identifier = NULL;
15838 static int snapshot_level_nr = -1;
15839
15840 static void SaveEngineSnapshotValues_RND(void)
15841 {
15842   static int belt_base_active_element[4] =
15843   {
15844     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15845     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15846     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15847     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15848   };
15849   int i, j;
15850
15851   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15852   {
15853     int element = EL_CUSTOM_START + i;
15854
15855     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15856   }
15857
15858   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15859   {
15860     int element = EL_GROUP_START + i;
15861
15862     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15863   }
15864
15865   for (i = 0; i < 4; i++)
15866   {
15867     for (j = 0; j < NUM_BELT_PARTS; j++)
15868     {
15869       int element = belt_base_active_element[i] + j;
15870       int graphic = el2img(element);
15871       int anim_mode = graphic_info[graphic].anim_mode;
15872
15873       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15874       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15875     }
15876   }
15877 }
15878
15879 static void LoadEngineSnapshotValues_RND(void)
15880 {
15881   unsigned int num_random_calls = game.num_random_calls;
15882   int i, j;
15883
15884   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15885   {
15886     int element = EL_CUSTOM_START + i;
15887
15888     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15889   }
15890
15891   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15892   {
15893     int element = EL_GROUP_START + i;
15894
15895     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15896   }
15897
15898   for (i = 0; i < 4; i++)
15899   {
15900     for (j = 0; j < NUM_BELT_PARTS; j++)
15901     {
15902       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15903       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15904
15905       graphic_info[graphic].anim_mode = anim_mode;
15906     }
15907   }
15908
15909   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15910   {
15911     InitRND(tape.random_seed);
15912     for (i = 0; i < num_random_calls; i++)
15913       RND(1);
15914   }
15915
15916   if (game.num_random_calls != num_random_calls)
15917   {
15918     Error("number of random calls out of sync");
15919     Error("number of random calls should be %d", num_random_calls);
15920     Error("number of random calls is %d", game.num_random_calls);
15921
15922     Fail("this should not happen -- please debug");
15923   }
15924 }
15925
15926 void FreeEngineSnapshotSingle(void)
15927 {
15928   FreeSnapshotSingle();
15929
15930   setString(&snapshot_level_identifier, NULL);
15931   snapshot_level_nr = -1;
15932 }
15933
15934 void FreeEngineSnapshotList(void)
15935 {
15936   FreeSnapshotList();
15937 }
15938
15939 static ListNode *SaveEngineSnapshotBuffers(void)
15940 {
15941   ListNode *buffers = NULL;
15942
15943   // copy some special values to a structure better suited for the snapshot
15944
15945   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15946     SaveEngineSnapshotValues_RND();
15947   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15948     SaveEngineSnapshotValues_EM();
15949   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15950     SaveEngineSnapshotValues_SP(&buffers);
15951   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15952     SaveEngineSnapshotValues_MM();
15953
15954   // save values stored in special snapshot structure
15955
15956   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15957     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15958   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15959     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15960   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15961     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15962   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15963     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15964
15965   // save further RND engine values
15966
15967   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15968   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15969   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15970
15971   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15972   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15973   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15974   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15975   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15976
15977   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15978   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15979   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15980
15981   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15982
15983   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15984   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15985
15986   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Tile));
15987   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15988   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15989   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15990   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15991   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15992   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15993   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15994   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15995   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15996   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15997   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15998   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15999   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
16000   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
16001   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
16002   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
16003   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
16004
16005   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
16006   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
16007
16008   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
16009   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
16010   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
16011
16012   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
16013   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
16014
16015   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
16016   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
16017   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandomStatic));
16018   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
16019   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
16020   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
16021
16022   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
16023   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
16024
16025 #if 0
16026   ListNode *node = engine_snapshot_list_rnd;
16027   int num_bytes = 0;
16028
16029   while (node != NULL)
16030   {
16031     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
16032
16033     node = node->next;
16034   }
16035
16036   Debug("game:playing:SaveEngineSnapshotBuffers",
16037         "size of engine snapshot: %d bytes", num_bytes);
16038 #endif
16039
16040   return buffers;
16041 }
16042
16043 void SaveEngineSnapshotSingle(void)
16044 {
16045   ListNode *buffers = SaveEngineSnapshotBuffers();
16046
16047   // finally save all snapshot buffers to single snapshot
16048   SaveSnapshotSingle(buffers);
16049
16050   // save level identification information
16051   setString(&snapshot_level_identifier, leveldir_current->identifier);
16052   snapshot_level_nr = level_nr;
16053 }
16054
16055 boolean CheckSaveEngineSnapshotToList(void)
16056 {
16057   boolean save_snapshot =
16058     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
16059      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
16060       game.snapshot.changed_action) ||
16061      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16062       game.snapshot.collected_item));
16063
16064   game.snapshot.changed_action = FALSE;
16065   game.snapshot.collected_item = FALSE;
16066   game.snapshot.save_snapshot = save_snapshot;
16067
16068   return save_snapshot;
16069 }
16070
16071 void SaveEngineSnapshotToList(void)
16072 {
16073   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
16074       tape.quick_resume)
16075     return;
16076
16077   ListNode *buffers = SaveEngineSnapshotBuffers();
16078
16079   // finally save all snapshot buffers to snapshot list
16080   SaveSnapshotToList(buffers);
16081 }
16082
16083 void SaveEngineSnapshotToListInitial(void)
16084 {
16085   FreeEngineSnapshotList();
16086
16087   SaveEngineSnapshotToList();
16088 }
16089
16090 static void LoadEngineSnapshotValues(void)
16091 {
16092   // restore special values from snapshot structure
16093
16094   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16095     LoadEngineSnapshotValues_RND();
16096   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
16097     LoadEngineSnapshotValues_EM();
16098   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
16099     LoadEngineSnapshotValues_SP();
16100   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
16101     LoadEngineSnapshotValues_MM();
16102 }
16103
16104 void LoadEngineSnapshotSingle(void)
16105 {
16106   LoadSnapshotSingle();
16107
16108   LoadEngineSnapshotValues();
16109 }
16110
16111 static void LoadEngineSnapshot_Undo(int steps)
16112 {
16113   LoadSnapshotFromList_Older(steps);
16114
16115   LoadEngineSnapshotValues();
16116 }
16117
16118 static void LoadEngineSnapshot_Redo(int steps)
16119 {
16120   LoadSnapshotFromList_Newer(steps);
16121
16122   LoadEngineSnapshotValues();
16123 }
16124
16125 boolean CheckEngineSnapshotSingle(void)
16126 {
16127   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16128           snapshot_level_nr == level_nr);
16129 }
16130
16131 boolean CheckEngineSnapshotList(void)
16132 {
16133   return CheckSnapshotList();
16134 }
16135
16136
16137 // ---------- new game button stuff -------------------------------------------
16138
16139 static struct
16140 {
16141   int graphic;
16142   struct XY *pos;
16143   int gadget_id;
16144   boolean *setup_value;
16145   boolean allowed_on_tape;
16146   boolean is_touch_button;
16147   char *infotext;
16148 } gamebutton_info[NUM_GAME_BUTTONS] =
16149 {
16150   {
16151     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
16152     GAME_CTRL_ID_STOP,                          NULL,
16153     TRUE, FALSE,                                "stop game"
16154   },
16155   {
16156     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
16157     GAME_CTRL_ID_PAUSE,                         NULL,
16158     TRUE, FALSE,                                "pause game"
16159   },
16160   {
16161     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
16162     GAME_CTRL_ID_PLAY,                          NULL,
16163     TRUE, FALSE,                                "play game"
16164   },
16165   {
16166     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
16167     GAME_CTRL_ID_UNDO,                          NULL,
16168     TRUE, FALSE,                                "undo step"
16169   },
16170   {
16171     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
16172     GAME_CTRL_ID_REDO,                          NULL,
16173     TRUE, FALSE,                                "redo step"
16174   },
16175   {
16176     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
16177     GAME_CTRL_ID_SAVE,                          NULL,
16178     TRUE, FALSE,                                "save game"
16179   },
16180   {
16181     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
16182     GAME_CTRL_ID_PAUSE2,                        NULL,
16183     TRUE, FALSE,                                "pause game"
16184   },
16185   {
16186     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
16187     GAME_CTRL_ID_LOAD,                          NULL,
16188     TRUE, FALSE,                                "load game"
16189   },
16190   {
16191     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
16192     GAME_CTRL_ID_PANEL_STOP,                    NULL,
16193     FALSE, FALSE,                               "stop game"
16194   },
16195   {
16196     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
16197     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
16198     FALSE, FALSE,                               "pause game"
16199   },
16200   {
16201     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
16202     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
16203     FALSE, FALSE,                               "play game"
16204   },
16205   {
16206     IMG_GFX_GAME_BUTTON_TOUCH_STOP,             &game.button.touch_stop,
16207     GAME_CTRL_ID_TOUCH_STOP,                    NULL,
16208     FALSE, TRUE,                                "stop game"
16209   },
16210   {
16211     IMG_GFX_GAME_BUTTON_TOUCH_PAUSE,            &game.button.touch_pause,
16212     GAME_CTRL_ID_TOUCH_PAUSE,                   NULL,
16213     FALSE, TRUE,                                "pause game"
16214   },
16215   {
16216     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
16217     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
16218     TRUE, FALSE,                                "background music on/off"
16219   },
16220   {
16221     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
16222     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
16223     TRUE, FALSE,                                "sound loops on/off"
16224   },
16225   {
16226     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
16227     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
16228     TRUE, FALSE,                                "normal sounds on/off"
16229   },
16230   {
16231     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
16232     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
16233     FALSE, FALSE,                               "background music on/off"
16234   },
16235   {
16236     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
16237     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
16238     FALSE, FALSE,                               "sound loops on/off"
16239   },
16240   {
16241     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
16242     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
16243     FALSE, FALSE,                               "normal sounds on/off"
16244   }
16245 };
16246
16247 void CreateGameButtons(void)
16248 {
16249   int i;
16250
16251   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16252   {
16253     int graphic = gamebutton_info[i].graphic;
16254     struct GraphicInfo *gfx = &graphic_info[graphic];
16255     struct XY *pos = gamebutton_info[i].pos;
16256     struct GadgetInfo *gi;
16257     int button_type;
16258     boolean checked;
16259     unsigned int event_mask;
16260     boolean is_touch_button = gamebutton_info[i].is_touch_button;
16261     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
16262     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
16263     int base_x = (is_touch_button ? 0 : on_tape ? VX : DX);
16264     int base_y = (is_touch_button ? 0 : on_tape ? VY : DY);
16265     int gd_x   = gfx->src_x;
16266     int gd_y   = gfx->src_y;
16267     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
16268     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
16269     int gd_xa  = gfx->src_x + gfx->active_xoffset;
16270     int gd_ya  = gfx->src_y + gfx->active_yoffset;
16271     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
16272     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
16273     int x = (is_touch_button ? pos->x : GDI_ACTIVE_POS(pos->x));
16274     int y = (is_touch_button ? pos->y : GDI_ACTIVE_POS(pos->y));
16275     int id = i;
16276
16277     // do not use touch buttons if overlay touch buttons are disabled
16278     if (is_touch_button && !setup.touch.overlay_buttons)
16279       continue;
16280
16281     if (gfx->bitmap == NULL)
16282     {
16283       game_gadget[id] = NULL;
16284
16285       continue;
16286     }
16287
16288     if (id == GAME_CTRL_ID_STOP ||
16289         id == GAME_CTRL_ID_PANEL_STOP ||
16290         id == GAME_CTRL_ID_TOUCH_STOP ||
16291         id == GAME_CTRL_ID_PLAY ||
16292         id == GAME_CTRL_ID_PANEL_PLAY ||
16293         id == GAME_CTRL_ID_SAVE ||
16294         id == GAME_CTRL_ID_LOAD)
16295     {
16296       button_type = GD_TYPE_NORMAL_BUTTON;
16297       checked = FALSE;
16298       event_mask = GD_EVENT_RELEASED;
16299     }
16300     else if (id == GAME_CTRL_ID_UNDO ||
16301              id == GAME_CTRL_ID_REDO)
16302     {
16303       button_type = GD_TYPE_NORMAL_BUTTON;
16304       checked = FALSE;
16305       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
16306     }
16307     else
16308     {
16309       button_type = GD_TYPE_CHECK_BUTTON;
16310       checked = (gamebutton_info[i].setup_value != NULL ?
16311                  *gamebutton_info[i].setup_value : FALSE);
16312       event_mask = GD_EVENT_PRESSED;
16313     }
16314
16315     gi = CreateGadget(GDI_CUSTOM_ID, id,
16316                       GDI_IMAGE_ID, graphic,
16317                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
16318                       GDI_X, base_x + x,
16319                       GDI_Y, base_y + y,
16320                       GDI_WIDTH, gfx->width,
16321                       GDI_HEIGHT, gfx->height,
16322                       GDI_TYPE, button_type,
16323                       GDI_STATE, GD_BUTTON_UNPRESSED,
16324                       GDI_CHECKED, checked,
16325                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
16326                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
16327                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
16328                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
16329                       GDI_DIRECT_DRAW, FALSE,
16330                       GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
16331                       GDI_EVENT_MASK, event_mask,
16332                       GDI_CALLBACK_ACTION, HandleGameButtons,
16333                       GDI_END);
16334
16335     if (gi == NULL)
16336       Fail("cannot create gadget");
16337
16338     game_gadget[id] = gi;
16339   }
16340 }
16341
16342 void FreeGameButtons(void)
16343 {
16344   int i;
16345
16346   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16347     FreeGadget(game_gadget[i]);
16348 }
16349
16350 static void UnmapGameButtonsAtSamePosition(int id)
16351 {
16352   int i;
16353
16354   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16355     if (i != id &&
16356         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
16357         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
16358       UnmapGadget(game_gadget[i]);
16359 }
16360
16361 static void UnmapGameButtonsAtSamePosition_All(void)
16362 {
16363   if (setup.show_load_save_buttons)
16364   {
16365     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16366     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16367     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16368   }
16369   else if (setup.show_undo_redo_buttons)
16370   {
16371     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16372     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16373     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16374   }
16375   else
16376   {
16377     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
16378     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
16379     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
16380
16381     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
16382     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
16383     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
16384   }
16385 }
16386
16387 void MapLoadSaveButtons(void)
16388 {
16389   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16390   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16391
16392   MapGadget(game_gadget[GAME_CTRL_ID_LOAD]);
16393   MapGadget(game_gadget[GAME_CTRL_ID_SAVE]);
16394 }
16395
16396 void MapUndoRedoButtons(void)
16397 {
16398   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16399   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16400
16401   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
16402   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
16403 }
16404
16405 void ModifyPauseButtons(void)
16406 {
16407   static int ids[] =
16408   {
16409     GAME_CTRL_ID_PAUSE,
16410     GAME_CTRL_ID_PAUSE2,
16411     GAME_CTRL_ID_PANEL_PAUSE,
16412     GAME_CTRL_ID_TOUCH_PAUSE,
16413     -1
16414   };
16415   int i;
16416
16417   for (i = 0; ids[i] > -1; i++)
16418     ModifyGadget(game_gadget[ids[i]], GDI_CHECKED, tape.pausing, GDI_END);
16419 }
16420
16421 static void MapGameButtonsExt(boolean on_tape)
16422 {
16423   int i;
16424
16425   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16426   {
16427     if ((i == GAME_CTRL_ID_UNDO ||
16428          i == GAME_CTRL_ID_REDO) &&
16429         game_status != GAME_MODE_PLAYING)
16430       continue;
16431
16432     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16433       MapGadget(game_gadget[i]);
16434   }
16435
16436   UnmapGameButtonsAtSamePosition_All();
16437
16438   RedrawGameButtons();
16439 }
16440
16441 static void UnmapGameButtonsExt(boolean on_tape)
16442 {
16443   int i;
16444
16445   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16446     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16447       UnmapGadget(game_gadget[i]);
16448 }
16449
16450 static void RedrawGameButtonsExt(boolean on_tape)
16451 {
16452   int i;
16453
16454   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16455     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16456       RedrawGadget(game_gadget[i]);
16457 }
16458
16459 static void SetGadgetState(struct GadgetInfo *gi, boolean state)
16460 {
16461   if (gi == NULL)
16462     return;
16463
16464   gi->checked = state;
16465 }
16466
16467 static void RedrawSoundButtonGadget(int id)
16468 {
16469   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
16470              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
16471              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
16472              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
16473              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
16474              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
16475              id);
16476
16477   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
16478   RedrawGadget(game_gadget[id2]);
16479 }
16480
16481 void MapGameButtons(void)
16482 {
16483   MapGameButtonsExt(FALSE);
16484 }
16485
16486 void UnmapGameButtons(void)
16487 {
16488   UnmapGameButtonsExt(FALSE);
16489 }
16490
16491 void RedrawGameButtons(void)
16492 {
16493   RedrawGameButtonsExt(FALSE);
16494 }
16495
16496 void MapGameButtonsOnTape(void)
16497 {
16498   MapGameButtonsExt(TRUE);
16499 }
16500
16501 void UnmapGameButtonsOnTape(void)
16502 {
16503   UnmapGameButtonsExt(TRUE);
16504 }
16505
16506 void RedrawGameButtonsOnTape(void)
16507 {
16508   RedrawGameButtonsExt(TRUE);
16509 }
16510
16511 static void GameUndoRedoExt(void)
16512 {
16513   ClearPlayerAction();
16514
16515   tape.pausing = TRUE;
16516
16517   RedrawPlayfield();
16518   UpdateAndDisplayGameControlValues();
16519
16520   DrawCompleteVideoDisplay();
16521   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
16522   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
16523   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
16524
16525   ModifyPauseButtons();
16526
16527   BackToFront();
16528 }
16529
16530 static void GameUndo(int steps)
16531 {
16532   if (!CheckEngineSnapshotList())
16533     return;
16534
16535   int tape_property_bits = tape.property_bits;
16536
16537   LoadEngineSnapshot_Undo(steps);
16538
16539   tape.property_bits |= tape_property_bits | TAPE_PROPERTY_SNAPSHOT;
16540
16541   GameUndoRedoExt();
16542 }
16543
16544 static void GameRedo(int steps)
16545 {
16546   if (!CheckEngineSnapshotList())
16547     return;
16548
16549   int tape_property_bits = tape.property_bits;
16550
16551   LoadEngineSnapshot_Redo(steps);
16552
16553   tape.property_bits |= tape_property_bits | TAPE_PROPERTY_SNAPSHOT;
16554
16555   GameUndoRedoExt();
16556 }
16557
16558 static void HandleGameButtonsExt(int id, int button)
16559 {
16560   static boolean game_undo_executed = FALSE;
16561   int steps = BUTTON_STEPSIZE(button);
16562   boolean handle_game_buttons =
16563     (game_status == GAME_MODE_PLAYING ||
16564      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
16565
16566   if (!handle_game_buttons)
16567     return;
16568
16569   switch (id)
16570   {
16571     case GAME_CTRL_ID_STOP:
16572     case GAME_CTRL_ID_PANEL_STOP:
16573     case GAME_CTRL_ID_TOUCH_STOP:
16574       TapeStopGame();
16575
16576       break;
16577
16578     case GAME_CTRL_ID_PAUSE:
16579     case GAME_CTRL_ID_PAUSE2:
16580     case GAME_CTRL_ID_PANEL_PAUSE:
16581     case GAME_CTRL_ID_TOUCH_PAUSE:
16582       if (network.enabled && game_status == GAME_MODE_PLAYING)
16583       {
16584         if (tape.pausing)
16585           SendToServer_ContinuePlaying();
16586         else
16587           SendToServer_PausePlaying();
16588       }
16589       else
16590         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16591
16592       game_undo_executed = FALSE;
16593
16594       break;
16595
16596     case GAME_CTRL_ID_PLAY:
16597     case GAME_CTRL_ID_PANEL_PLAY:
16598       if (game_status == GAME_MODE_MAIN)
16599       {
16600         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
16601       }
16602       else if (tape.pausing)
16603       {
16604         if (network.enabled)
16605           SendToServer_ContinuePlaying();
16606         else
16607           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
16608       }
16609       break;
16610
16611     case GAME_CTRL_ID_UNDO:
16612       // Important: When using "save snapshot when collecting an item" mode,
16613       // load last (current) snapshot for first "undo" after pressing "pause"
16614       // (else the last-but-one snapshot would be loaded, because the snapshot
16615       // pointer already points to the last snapshot when pressing "pause",
16616       // which is fine for "every step/move" mode, but not for "every collect")
16617       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16618           !game_undo_executed)
16619         steps--;
16620
16621       game_undo_executed = TRUE;
16622
16623       GameUndo(steps);
16624       break;
16625
16626     case GAME_CTRL_ID_REDO:
16627       GameRedo(steps);
16628       break;
16629
16630     case GAME_CTRL_ID_SAVE:
16631       TapeQuickSave();
16632       break;
16633
16634     case GAME_CTRL_ID_LOAD:
16635       TapeQuickLoad();
16636       break;
16637
16638     case SOUND_CTRL_ID_MUSIC:
16639     case SOUND_CTRL_ID_PANEL_MUSIC:
16640       if (setup.sound_music)
16641       { 
16642         setup.sound_music = FALSE;
16643
16644         FadeMusic();
16645       }
16646       else if (audio.music_available)
16647       { 
16648         setup.sound = setup.sound_music = TRUE;
16649
16650         SetAudioMode(setup.sound);
16651
16652         if (game_status == GAME_MODE_PLAYING)
16653           PlayLevelMusic();
16654       }
16655
16656       RedrawSoundButtonGadget(id);
16657
16658       break;
16659
16660     case SOUND_CTRL_ID_LOOPS:
16661     case SOUND_CTRL_ID_PANEL_LOOPS:
16662       if (setup.sound_loops)
16663         setup.sound_loops = FALSE;
16664       else if (audio.loops_available)
16665       {
16666         setup.sound = setup.sound_loops = TRUE;
16667
16668         SetAudioMode(setup.sound);
16669       }
16670
16671       RedrawSoundButtonGadget(id);
16672
16673       break;
16674
16675     case SOUND_CTRL_ID_SIMPLE:
16676     case SOUND_CTRL_ID_PANEL_SIMPLE:
16677       if (setup.sound_simple)
16678         setup.sound_simple = FALSE;
16679       else if (audio.sound_available)
16680       {
16681         setup.sound = setup.sound_simple = TRUE;
16682
16683         SetAudioMode(setup.sound);
16684       }
16685
16686       RedrawSoundButtonGadget(id);
16687
16688       break;
16689
16690     default:
16691       break;
16692   }
16693 }
16694
16695 static void HandleGameButtons(struct GadgetInfo *gi)
16696 {
16697   HandleGameButtonsExt(gi->custom_id, gi->event.button);
16698 }
16699
16700 void HandleSoundButtonKeys(Key key)
16701 {
16702   if (key == setup.shortcut.sound_simple)
16703     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
16704   else if (key == setup.shortcut.sound_loops)
16705     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
16706   else if (key == setup.shortcut.sound_music)
16707     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
16708 }