e43739030777399cda01f06e2f86ef62833cd187
[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 //                  http://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_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
883                                  RND(element_info[e].ce_value_random_initial))
884 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
885 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
886                                  RND((c)->delay_random * (c)->delay_frames))
887 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
888                                  RND((c)->delay_random))
889
890
891 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
892          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
893
894 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
895         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
896          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
897          (be) + (e) - EL_SELF)
898
899 #define GET_PLAYER_FROM_BITS(p)                                         \
900         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
901
902 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
903         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
904          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
905          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
906          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
907          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
908          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
909          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
910          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
911          (e))
912
913 #define CAN_GROW_INTO(e)                                                \
914         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
915
916 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
917                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
918                                         (condition)))
919
920 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
921                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
922                                         (CAN_MOVE_INTO_ACID(e) &&       \
923                                          Feld[x][y] == EL_ACID) ||      \
924                                         (condition)))
925
926 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
927                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
928                                         (CAN_MOVE_INTO_ACID(e) &&       \
929                                          Feld[x][y] == EL_ACID) ||      \
930                                         (condition)))
931
932 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
933                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
934                                         (condition) ||                  \
935                                         (CAN_MOVE_INTO_ACID(e) &&       \
936                                          Feld[x][y] == EL_ACID) ||      \
937                                         (DONT_COLLIDE_WITH(e) &&        \
938                                          IS_PLAYER(x, y) &&             \
939                                          !PLAYER_ENEMY_PROTECTED(x, y))))
940
941 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
942         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
943
944 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
945         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
946
947 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
948         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
949
950 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
951         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
952                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
953
954 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
955         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
956
957 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
958         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
959
960 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
961         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
962
963 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
964         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
965
966 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
967         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
968
969 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
970         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
971                                                  Feld[x][y] == EL_EM_EXIT_OPEN || \
972                                                  Feld[x][y] == EL_STEEL_EXIT_OPEN || \
973                                                  Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
974                                                  IS_FOOD_PENGUIN(Feld[x][y])))
975 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
976         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
977
978 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
979         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
980
981 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
982         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
983
984 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
985         (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER ||   \
986                                 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
987
988 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
989
990 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
991                 (!IS_PLAYER(x, y) &&                                    \
992                  IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
993
994 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
995         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
996
997 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
998 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
999
1000 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
1001 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
1002 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
1003 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1004
1005 #define MM_HEALTH(x)            (MIN(MAX(0, MAX_HEALTH - (x)), MAX_HEALTH))
1006
1007 // game button identifiers
1008 #define GAME_CTRL_ID_STOP               0
1009 #define GAME_CTRL_ID_PAUSE              1
1010 #define GAME_CTRL_ID_PLAY               2
1011 #define GAME_CTRL_ID_UNDO               3
1012 #define GAME_CTRL_ID_REDO               4
1013 #define GAME_CTRL_ID_SAVE               5
1014 #define GAME_CTRL_ID_PAUSE2             6
1015 #define GAME_CTRL_ID_LOAD               7
1016 #define GAME_CTRL_ID_PANEL_STOP         8
1017 #define GAME_CTRL_ID_PANEL_PAUSE        9
1018 #define GAME_CTRL_ID_PANEL_PLAY         10
1019 #define GAME_CTRL_ID_TOUCH_STOP         11
1020 #define GAME_CTRL_ID_TOUCH_PAUSE        12
1021 #define SOUND_CTRL_ID_MUSIC             13
1022 #define SOUND_CTRL_ID_LOOPS             14
1023 #define SOUND_CTRL_ID_SIMPLE            15
1024 #define SOUND_CTRL_ID_PANEL_MUSIC       16
1025 #define SOUND_CTRL_ID_PANEL_LOOPS       17
1026 #define SOUND_CTRL_ID_PANEL_SIMPLE      18
1027
1028 #define NUM_GAME_BUTTONS                19
1029
1030
1031 // forward declaration for internal use
1032
1033 static void CreateField(int, int, int);
1034
1035 static void ResetGfxAnimation(int, int);
1036
1037 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1038 static void AdvanceFrameAndPlayerCounters(int);
1039
1040 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1041 static boolean MovePlayer(struct PlayerInfo *, int, int);
1042 static void ScrollPlayer(struct PlayerInfo *, int);
1043 static void ScrollScreen(struct PlayerInfo *, int);
1044
1045 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1046 static boolean DigFieldByCE(int, int, int);
1047 static boolean SnapField(struct PlayerInfo *, int, int);
1048 static boolean DropElement(struct PlayerInfo *);
1049
1050 static void InitBeltMovement(void);
1051 static void CloseAllOpenTimegates(void);
1052 static void CheckGravityMovement(struct PlayerInfo *);
1053 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1054 static void KillPlayerUnlessEnemyProtected(int, int);
1055 static void KillPlayerUnlessExplosionProtected(int, int);
1056
1057 static void TestIfPlayerTouchesCustomElement(int, int);
1058 static void TestIfElementTouchesCustomElement(int, int);
1059 static void TestIfElementHitsCustomElement(int, int, int);
1060
1061 static void HandleElementChange(int, int, int);
1062 static void ExecuteCustomElementAction(int, int, int, int);
1063 static boolean ChangeElement(int, int, int, int);
1064
1065 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1066 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1067         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1068 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1069         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1070 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1071         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1072 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1073         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1074
1075 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1076 #define CheckElementChange(x, y, e, te, ev)                             \
1077         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1078 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1079         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1080 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1081         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1082
1083 static void PlayLevelSound(int, int, int);
1084 static void PlayLevelSoundNearest(int, int, int);
1085 static void PlayLevelSoundAction(int, int, int);
1086 static void PlayLevelSoundElementAction(int, int, int, int);
1087 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1088 static void PlayLevelSoundActionIfLoop(int, int, int);
1089 static void StopLevelSoundActionIfLoop(int, int, int);
1090 static void PlayLevelMusic(void);
1091 static void FadeLevelSoundsAndMusic(void);
1092
1093 static void HandleGameButtons(struct GadgetInfo *);
1094
1095 int AmoebeNachbarNr(int, int);
1096 void AmoebeUmwandeln(int, int);
1097 void ContinueMoving(int, int);
1098 void Bang(int, int);
1099 void InitMovDir(int, int);
1100 void InitAmoebaNr(int, int);
1101 int NewHiScore(int);
1102
1103 void TestIfGoodThingHitsBadThing(int, int, int);
1104 void TestIfBadThingHitsGoodThing(int, int, int);
1105 void TestIfPlayerTouchesBadThing(int, int);
1106 void TestIfPlayerRunsIntoBadThing(int, int, int);
1107 void TestIfBadThingTouchesPlayer(int, int);
1108 void TestIfBadThingRunsIntoPlayer(int, int, int);
1109 void TestIfFriendTouchesBadThing(int, int);
1110 void TestIfBadThingTouchesFriend(int, int);
1111 void TestIfBadThingTouchesOtherBadThing(int, int);
1112 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1113
1114 void KillPlayer(struct PlayerInfo *);
1115 void BuryPlayer(struct PlayerInfo *);
1116 void RemovePlayer(struct PlayerInfo *);
1117 void ExitPlayer(struct PlayerInfo *);
1118
1119 static int getInvisibleActiveFromInvisibleElement(int);
1120 static int getInvisibleFromInvisibleActiveElement(int);
1121
1122 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1123
1124 // for detection of endless loops, caused by custom element programming
1125 // (using maximal playfield width x 10 is just a rough approximation)
1126 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1127
1128 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1129 {                                                                       \
1130   if (recursion_loop_detected)                                          \
1131     return (rc);                                                        \
1132                                                                         \
1133   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1134   {                                                                     \
1135     recursion_loop_detected = TRUE;                                     \
1136     recursion_loop_element = (e);                                       \
1137   }                                                                     \
1138                                                                         \
1139   recursion_loop_depth++;                                               \
1140 }
1141
1142 #define RECURSION_LOOP_DETECTION_END()                                  \
1143 {                                                                       \
1144   recursion_loop_depth--;                                               \
1145 }
1146
1147 static int recursion_loop_depth;
1148 static boolean recursion_loop_detected;
1149 static boolean recursion_loop_element;
1150
1151 static int map_player_action[MAX_PLAYERS];
1152
1153
1154 // ----------------------------------------------------------------------------
1155 // definition of elements that automatically change to other elements after
1156 // a specified time, eventually calling a function when changing
1157 // ----------------------------------------------------------------------------
1158
1159 // forward declaration for changer functions
1160 static void InitBuggyBase(int, int);
1161 static void WarnBuggyBase(int, int);
1162
1163 static void InitTrap(int, int);
1164 static void ActivateTrap(int, int);
1165 static void ChangeActiveTrap(int, int);
1166
1167 static void InitRobotWheel(int, int);
1168 static void RunRobotWheel(int, int);
1169 static void StopRobotWheel(int, int);
1170
1171 static void InitTimegateWheel(int, int);
1172 static void RunTimegateWheel(int, int);
1173
1174 static void InitMagicBallDelay(int, int);
1175 static void ActivateMagicBall(int, int);
1176
1177 struct ChangingElementInfo
1178 {
1179   int element;
1180   int target_element;
1181   int change_delay;
1182   void (*pre_change_function)(int x, int y);
1183   void (*change_function)(int x, int y);
1184   void (*post_change_function)(int x, int y);
1185 };
1186
1187 static struct ChangingElementInfo change_delay_list[] =
1188 {
1189   {
1190     EL_NUT_BREAKING,
1191     EL_EMERALD,
1192     6,
1193     NULL,
1194     NULL,
1195     NULL
1196   },
1197   {
1198     EL_PEARL_BREAKING,
1199     EL_EMPTY,
1200     8,
1201     NULL,
1202     NULL,
1203     NULL
1204   },
1205   {
1206     EL_EXIT_OPENING,
1207     EL_EXIT_OPEN,
1208     29,
1209     NULL,
1210     NULL,
1211     NULL
1212   },
1213   {
1214     EL_EXIT_CLOSING,
1215     EL_EXIT_CLOSED,
1216     29,
1217     NULL,
1218     NULL,
1219     NULL
1220   },
1221   {
1222     EL_STEEL_EXIT_OPENING,
1223     EL_STEEL_EXIT_OPEN,
1224     29,
1225     NULL,
1226     NULL,
1227     NULL
1228   },
1229   {
1230     EL_STEEL_EXIT_CLOSING,
1231     EL_STEEL_EXIT_CLOSED,
1232     29,
1233     NULL,
1234     NULL,
1235     NULL
1236   },
1237   {
1238     EL_EM_EXIT_OPENING,
1239     EL_EM_EXIT_OPEN,
1240     29,
1241     NULL,
1242     NULL,
1243     NULL
1244   },
1245   {
1246     EL_EM_EXIT_CLOSING,
1247     EL_EMPTY,
1248     29,
1249     NULL,
1250     NULL,
1251     NULL
1252   },
1253   {
1254     EL_EM_STEEL_EXIT_OPENING,
1255     EL_EM_STEEL_EXIT_OPEN,
1256     29,
1257     NULL,
1258     NULL,
1259     NULL
1260   },
1261   {
1262     EL_EM_STEEL_EXIT_CLOSING,
1263     EL_STEELWALL,
1264     29,
1265     NULL,
1266     NULL,
1267     NULL
1268   },
1269   {
1270     EL_SP_EXIT_OPENING,
1271     EL_SP_EXIT_OPEN,
1272     29,
1273     NULL,
1274     NULL,
1275     NULL
1276   },
1277   {
1278     EL_SP_EXIT_CLOSING,
1279     EL_SP_EXIT_CLOSED,
1280     29,
1281     NULL,
1282     NULL,
1283     NULL
1284   },
1285   {
1286     EL_SWITCHGATE_OPENING,
1287     EL_SWITCHGATE_OPEN,
1288     29,
1289     NULL,
1290     NULL,
1291     NULL
1292   },
1293   {
1294     EL_SWITCHGATE_CLOSING,
1295     EL_SWITCHGATE_CLOSED,
1296     29,
1297     NULL,
1298     NULL,
1299     NULL
1300   },
1301   {
1302     EL_TIMEGATE_OPENING,
1303     EL_TIMEGATE_OPEN,
1304     29,
1305     NULL,
1306     NULL,
1307     NULL
1308   },
1309   {
1310     EL_TIMEGATE_CLOSING,
1311     EL_TIMEGATE_CLOSED,
1312     29,
1313     NULL,
1314     NULL,
1315     NULL
1316   },
1317
1318   {
1319     EL_ACID_SPLASH_LEFT,
1320     EL_EMPTY,
1321     8,
1322     NULL,
1323     NULL,
1324     NULL
1325   },
1326   {
1327     EL_ACID_SPLASH_RIGHT,
1328     EL_EMPTY,
1329     8,
1330     NULL,
1331     NULL,
1332     NULL
1333   },
1334   {
1335     EL_SP_BUGGY_BASE,
1336     EL_SP_BUGGY_BASE_ACTIVATING,
1337     0,
1338     InitBuggyBase,
1339     NULL,
1340     NULL
1341   },
1342   {
1343     EL_SP_BUGGY_BASE_ACTIVATING,
1344     EL_SP_BUGGY_BASE_ACTIVE,
1345     0,
1346     InitBuggyBase,
1347     NULL,
1348     NULL
1349   },
1350   {
1351     EL_SP_BUGGY_BASE_ACTIVE,
1352     EL_SP_BUGGY_BASE,
1353     0,
1354     InitBuggyBase,
1355     WarnBuggyBase,
1356     NULL
1357   },
1358   {
1359     EL_TRAP,
1360     EL_TRAP_ACTIVE,
1361     0,
1362     InitTrap,
1363     NULL,
1364     ActivateTrap
1365   },
1366   {
1367     EL_TRAP_ACTIVE,
1368     EL_TRAP,
1369     31,
1370     NULL,
1371     ChangeActiveTrap,
1372     NULL
1373   },
1374   {
1375     EL_ROBOT_WHEEL_ACTIVE,
1376     EL_ROBOT_WHEEL,
1377     0,
1378     InitRobotWheel,
1379     RunRobotWheel,
1380     StopRobotWheel
1381   },
1382   {
1383     EL_TIMEGATE_SWITCH_ACTIVE,
1384     EL_TIMEGATE_SWITCH,
1385     0,
1386     InitTimegateWheel,
1387     RunTimegateWheel,
1388     NULL
1389   },
1390   {
1391     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1392     EL_DC_TIMEGATE_SWITCH,
1393     0,
1394     InitTimegateWheel,
1395     RunTimegateWheel,
1396     NULL
1397   },
1398   {
1399     EL_EMC_MAGIC_BALL_ACTIVE,
1400     EL_EMC_MAGIC_BALL_ACTIVE,
1401     0,
1402     InitMagicBallDelay,
1403     NULL,
1404     ActivateMagicBall
1405   },
1406   {
1407     EL_EMC_SPRING_BUMPER_ACTIVE,
1408     EL_EMC_SPRING_BUMPER,
1409     8,
1410     NULL,
1411     NULL,
1412     NULL
1413   },
1414   {
1415     EL_DIAGONAL_SHRINKING,
1416     EL_UNDEFINED,
1417     0,
1418     NULL,
1419     NULL,
1420     NULL
1421   },
1422   {
1423     EL_DIAGONAL_GROWING,
1424     EL_UNDEFINED,
1425     0,
1426     NULL,
1427     NULL,
1428     NULL,
1429   },
1430
1431   {
1432     EL_UNDEFINED,
1433     EL_UNDEFINED,
1434     -1,
1435     NULL,
1436     NULL,
1437     NULL
1438   }
1439 };
1440
1441 struct
1442 {
1443   int element;
1444   int push_delay_fixed, push_delay_random;
1445 }
1446 push_delay_list[] =
1447 {
1448   { EL_SPRING,                  0, 0 },
1449   { EL_BALLOON,                 0, 0 },
1450
1451   { EL_SOKOBAN_OBJECT,          2, 0 },
1452   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1453   { EL_SATELLITE,               2, 0 },
1454   { EL_SP_DISK_YELLOW,          2, 0 },
1455
1456   { EL_UNDEFINED,               0, 0 },
1457 };
1458
1459 struct
1460 {
1461   int element;
1462   int move_stepsize;
1463 }
1464 move_stepsize_list[] =
1465 {
1466   { EL_AMOEBA_DROP,             2 },
1467   { EL_AMOEBA_DROPPING,         2 },
1468   { EL_QUICKSAND_FILLING,       1 },
1469   { EL_QUICKSAND_EMPTYING,      1 },
1470   { EL_QUICKSAND_FAST_FILLING,  2 },
1471   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1472   { EL_MAGIC_WALL_FILLING,      2 },
1473   { EL_MAGIC_WALL_EMPTYING,     2 },
1474   { EL_BD_MAGIC_WALL_FILLING,   2 },
1475   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1476   { EL_DC_MAGIC_WALL_FILLING,   2 },
1477   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1478
1479   { EL_UNDEFINED,               0 },
1480 };
1481
1482 struct
1483 {
1484   int element;
1485   int count;
1486 }
1487 collect_count_list[] =
1488 {
1489   { EL_EMERALD,                 1 },
1490   { EL_BD_DIAMOND,              1 },
1491   { EL_EMERALD_YELLOW,          1 },
1492   { EL_EMERALD_RED,             1 },
1493   { EL_EMERALD_PURPLE,          1 },
1494   { EL_DIAMOND,                 3 },
1495   { EL_SP_INFOTRON,             1 },
1496   { EL_PEARL,                   5 },
1497   { EL_CRYSTAL,                 8 },
1498
1499   { EL_UNDEFINED,               0 },
1500 };
1501
1502 struct
1503 {
1504   int element;
1505   int direction;
1506 }
1507 access_direction_list[] =
1508 {
1509   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1510   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1511   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1512   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1513   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1514   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1515   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1516   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1517   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1518   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1519   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1520
1521   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1522   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1523   { EL_SP_PORT_UP,                                                   MV_DOWN },
1524   { EL_SP_PORT_DOWN,                                         MV_UP           },
1525   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1526   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1527   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1528   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1529   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1530   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1531   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1532   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1533   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1534   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1535   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1536   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1537   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1538   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1539   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1540
1541   { EL_UNDEFINED,                       MV_NONE                              }
1542 };
1543
1544 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1545
1546 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1547 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1548 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1549                                  IS_JUST_CHANGING(x, y))
1550
1551 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1552
1553 // static variables for playfield scan mode (scanning forward or backward)
1554 static int playfield_scan_start_x = 0;
1555 static int playfield_scan_start_y = 0;
1556 static int playfield_scan_delta_x = 1;
1557 static int playfield_scan_delta_y = 1;
1558
1559 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1560                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1561                                      (y) += playfield_scan_delta_y)     \
1562                                 for ((x) = playfield_scan_start_x;      \
1563                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1564                                      (x) += playfield_scan_delta_x)
1565
1566 #ifdef DEBUG
1567 void DEBUG_SetMaximumDynamite(void)
1568 {
1569   int i;
1570
1571   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1572     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1573       local_player->inventory_element[local_player->inventory_size++] =
1574         EL_DYNAMITE;
1575 }
1576 #endif
1577
1578 static void InitPlayfieldScanModeVars(void)
1579 {
1580   if (game.use_reverse_scan_direction)
1581   {
1582     playfield_scan_start_x = lev_fieldx - 1;
1583     playfield_scan_start_y = lev_fieldy - 1;
1584
1585     playfield_scan_delta_x = -1;
1586     playfield_scan_delta_y = -1;
1587   }
1588   else
1589   {
1590     playfield_scan_start_x = 0;
1591     playfield_scan_start_y = 0;
1592
1593     playfield_scan_delta_x = 1;
1594     playfield_scan_delta_y = 1;
1595   }
1596 }
1597
1598 static void InitPlayfieldScanMode(int mode)
1599 {
1600   game.use_reverse_scan_direction =
1601     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1602
1603   InitPlayfieldScanModeVars();
1604 }
1605
1606 static int get_move_delay_from_stepsize(int move_stepsize)
1607 {
1608   move_stepsize =
1609     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1610
1611   // make sure that stepsize value is always a power of 2
1612   move_stepsize = (1 << log_2(move_stepsize));
1613
1614   return TILEX / move_stepsize;
1615 }
1616
1617 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1618                                boolean init_game)
1619 {
1620   int player_nr = player->index_nr;
1621   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1622   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1623
1624   // do no immediately change move delay -- the player might just be moving
1625   player->move_delay_value_next = move_delay;
1626
1627   // information if player can move must be set separately
1628   player->cannot_move = cannot_move;
1629
1630   if (init_game)
1631   {
1632     player->move_delay       = game.initial_move_delay[player_nr];
1633     player->move_delay_value = game.initial_move_delay_value[player_nr];
1634
1635     player->move_delay_value_next = -1;
1636
1637     player->move_delay_reset_counter = 0;
1638   }
1639 }
1640
1641 void GetPlayerConfig(void)
1642 {
1643   GameFrameDelay = setup.game_frame_delay;
1644
1645   if (!audio.sound_available)
1646     setup.sound_simple = FALSE;
1647
1648   if (!audio.loops_available)
1649     setup.sound_loops = FALSE;
1650
1651   if (!audio.music_available)
1652     setup.sound_music = FALSE;
1653
1654   if (!video.fullscreen_available)
1655     setup.fullscreen = FALSE;
1656
1657   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1658
1659   SetAudioMode(setup.sound);
1660 }
1661
1662 int GetElementFromGroupElement(int element)
1663 {
1664   if (IS_GROUP_ELEMENT(element))
1665   {
1666     struct ElementGroupInfo *group = element_info[element].group;
1667     int last_anim_random_frame = gfx.anim_random_frame;
1668     int element_pos;
1669
1670     if (group->choice_mode == ANIM_RANDOM)
1671       gfx.anim_random_frame = RND(group->num_elements_resolved);
1672
1673     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1674                                     group->choice_mode, 0,
1675                                     group->choice_pos);
1676
1677     if (group->choice_mode == ANIM_RANDOM)
1678       gfx.anim_random_frame = last_anim_random_frame;
1679
1680     group->choice_pos++;
1681
1682     element = group->element_resolved[element_pos];
1683   }
1684
1685   return element;
1686 }
1687
1688 static void IncrementSokobanFieldsNeeded(void)
1689 {
1690   if (level.sb_fields_needed)
1691     game.sokoban_fields_still_needed++;
1692 }
1693
1694 static void IncrementSokobanObjectsNeeded(void)
1695 {
1696   if (level.sb_objects_needed)
1697     game.sokoban_objects_still_needed++;
1698 }
1699
1700 static void DecrementSokobanFieldsNeeded(void)
1701 {
1702   if (game.sokoban_fields_still_needed > 0)
1703     game.sokoban_fields_still_needed--;
1704 }
1705
1706 static void DecrementSokobanObjectsNeeded(void)
1707 {
1708   if (game.sokoban_objects_still_needed > 0)
1709     game.sokoban_objects_still_needed--;
1710 }
1711
1712 static void InitPlayerField(int x, int y, int element, boolean init_game)
1713 {
1714   if (element == EL_SP_MURPHY)
1715   {
1716     if (init_game)
1717     {
1718       if (stored_player[0].present)
1719       {
1720         Feld[x][y] = EL_SP_MURPHY_CLONE;
1721
1722         return;
1723       }
1724       else
1725       {
1726         stored_player[0].initial_element = element;
1727         stored_player[0].use_murphy = TRUE;
1728
1729         if (!level.use_artwork_element[0])
1730           stored_player[0].artwork_element = EL_SP_MURPHY;
1731       }
1732
1733       Feld[x][y] = EL_PLAYER_1;
1734     }
1735   }
1736
1737   if (init_game)
1738   {
1739     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1740     int jx = player->jx, jy = player->jy;
1741
1742     player->present = TRUE;
1743
1744     player->block_last_field = (element == EL_SP_MURPHY ?
1745                                 level.sp_block_last_field :
1746                                 level.block_last_field);
1747
1748     // ---------- initialize player's last field block delay ------------------
1749
1750     // always start with reliable default value (no adjustment needed)
1751     player->block_delay_adjustment = 0;
1752
1753     // special case 1: in Supaplex, Murphy blocks last field one more frame
1754     if (player->block_last_field && element == EL_SP_MURPHY)
1755       player->block_delay_adjustment = 1;
1756
1757     // special case 2: in game engines before 3.1.1, blocking was different
1758     if (game.use_block_last_field_bug)
1759       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1760
1761     if (!network.enabled || player->connected_network)
1762     {
1763       player->active = TRUE;
1764
1765       // remove potentially duplicate players
1766       if (StorePlayer[jx][jy] == Feld[x][y])
1767         StorePlayer[jx][jy] = 0;
1768
1769       StorePlayer[x][y] = Feld[x][y];
1770
1771 #if DEBUG_INIT_PLAYER
1772       if (options.debug)
1773       {
1774         printf("- player element %d activated", player->element_nr);
1775         printf(" (local player is %d and currently %s)\n",
1776                local_player->element_nr,
1777                local_player->active ? "active" : "not active");
1778       }
1779     }
1780 #endif
1781
1782     Feld[x][y] = EL_EMPTY;
1783
1784     player->jx = player->last_jx = x;
1785     player->jy = player->last_jy = y;
1786   }
1787
1788   if (!init_game)
1789   {
1790     int player_nr = GET_PLAYER_NR(element);
1791     struct PlayerInfo *player = &stored_player[player_nr];
1792
1793     if (player->active && player->killed)
1794       player->reanimated = TRUE; // if player was just killed, reanimate him
1795   }
1796 }
1797
1798 static void InitField(int x, int y, boolean init_game)
1799 {
1800   int element = Feld[x][y];
1801
1802   switch (element)
1803   {
1804     case EL_SP_MURPHY:
1805     case EL_PLAYER_1:
1806     case EL_PLAYER_2:
1807     case EL_PLAYER_3:
1808     case EL_PLAYER_4:
1809       InitPlayerField(x, y, element, init_game);
1810       break;
1811
1812     case EL_SOKOBAN_FIELD_PLAYER:
1813       element = Feld[x][y] = EL_PLAYER_1;
1814       InitField(x, y, init_game);
1815
1816       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1817       InitField(x, y, init_game);
1818       break;
1819
1820     case EL_SOKOBAN_FIELD_EMPTY:
1821       IncrementSokobanFieldsNeeded();
1822       break;
1823
1824     case EL_SOKOBAN_OBJECT:
1825       IncrementSokobanObjectsNeeded();
1826       break;
1827
1828     case EL_STONEBLOCK:
1829       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1830         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1831       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1832         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1833       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1834         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1835       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1836         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1837       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1838         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1839       break;
1840
1841     case EL_BUG:
1842     case EL_BUG_RIGHT:
1843     case EL_BUG_UP:
1844     case EL_BUG_LEFT:
1845     case EL_BUG_DOWN:
1846     case EL_SPACESHIP:
1847     case EL_SPACESHIP_RIGHT:
1848     case EL_SPACESHIP_UP:
1849     case EL_SPACESHIP_LEFT:
1850     case EL_SPACESHIP_DOWN:
1851     case EL_BD_BUTTERFLY:
1852     case EL_BD_BUTTERFLY_RIGHT:
1853     case EL_BD_BUTTERFLY_UP:
1854     case EL_BD_BUTTERFLY_LEFT:
1855     case EL_BD_BUTTERFLY_DOWN:
1856     case EL_BD_FIREFLY:
1857     case EL_BD_FIREFLY_RIGHT:
1858     case EL_BD_FIREFLY_UP:
1859     case EL_BD_FIREFLY_LEFT:
1860     case EL_BD_FIREFLY_DOWN:
1861     case EL_PACMAN_RIGHT:
1862     case EL_PACMAN_UP:
1863     case EL_PACMAN_LEFT:
1864     case EL_PACMAN_DOWN:
1865     case EL_YAMYAM:
1866     case EL_YAMYAM_LEFT:
1867     case EL_YAMYAM_RIGHT:
1868     case EL_YAMYAM_UP:
1869     case EL_YAMYAM_DOWN:
1870     case EL_DARK_YAMYAM:
1871     case EL_ROBOT:
1872     case EL_PACMAN:
1873     case EL_SP_SNIKSNAK:
1874     case EL_SP_ELECTRON:
1875     case EL_MOLE:
1876     case EL_MOLE_LEFT:
1877     case EL_MOLE_RIGHT:
1878     case EL_MOLE_UP:
1879     case EL_MOLE_DOWN:
1880       InitMovDir(x, y);
1881       break;
1882
1883     case EL_AMOEBA_FULL:
1884     case EL_BD_AMOEBA:
1885       InitAmoebaNr(x, y);
1886       break;
1887
1888     case EL_AMOEBA_DROP:
1889       if (y == lev_fieldy - 1)
1890       {
1891         Feld[x][y] = EL_AMOEBA_GROWING;
1892         Store[x][y] = EL_AMOEBA_WET;
1893       }
1894       break;
1895
1896     case EL_DYNAMITE_ACTIVE:
1897     case EL_SP_DISK_RED_ACTIVE:
1898     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1899     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1900     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1901     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1902       MovDelay[x][y] = 96;
1903       break;
1904
1905     case EL_EM_DYNAMITE_ACTIVE:
1906       MovDelay[x][y] = 32;
1907       break;
1908
1909     case EL_LAMP:
1910       game.lights_still_needed++;
1911       break;
1912
1913     case EL_PENGUIN:
1914       game.friends_still_needed++;
1915       break;
1916
1917     case EL_PIG:
1918     case EL_DRAGON:
1919       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1920       break;
1921
1922     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1923     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1924     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1925     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1926     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1927     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1928     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1929     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1930     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1931     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1932     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1933     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1934       if (init_game)
1935       {
1936         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1937         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1938         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1939
1940         if (game.belt_dir_nr[belt_nr] == 3)     // initial value
1941         {
1942           game.belt_dir[belt_nr] = belt_dir;
1943           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1944         }
1945         else    // more than one switch -- set it like the first switch
1946         {
1947           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1948         }
1949       }
1950       break;
1951
1952     case EL_LIGHT_SWITCH_ACTIVE:
1953       if (init_game)
1954         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1955       break;
1956
1957     case EL_INVISIBLE_STEELWALL:
1958     case EL_INVISIBLE_WALL:
1959     case EL_INVISIBLE_SAND:
1960       if (game.light_time_left > 0 ||
1961           game.lenses_time_left > 0)
1962         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1963       break;
1964
1965     case EL_EMC_MAGIC_BALL:
1966       if (game.ball_active)
1967         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1968       break;
1969
1970     case EL_EMC_MAGIC_BALL_SWITCH:
1971       if (game.ball_active)
1972         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1973       break;
1974
1975     case EL_TRIGGER_PLAYER:
1976     case EL_TRIGGER_ELEMENT:
1977     case EL_TRIGGER_CE_VALUE:
1978     case EL_TRIGGER_CE_SCORE:
1979     case EL_SELF:
1980     case EL_ANY_ELEMENT:
1981     case EL_CURRENT_CE_VALUE:
1982     case EL_CURRENT_CE_SCORE:
1983     case EL_PREV_CE_1:
1984     case EL_PREV_CE_2:
1985     case EL_PREV_CE_3:
1986     case EL_PREV_CE_4:
1987     case EL_PREV_CE_5:
1988     case EL_PREV_CE_6:
1989     case EL_PREV_CE_7:
1990     case EL_PREV_CE_8:
1991     case EL_NEXT_CE_1:
1992     case EL_NEXT_CE_2:
1993     case EL_NEXT_CE_3:
1994     case EL_NEXT_CE_4:
1995     case EL_NEXT_CE_5:
1996     case EL_NEXT_CE_6:
1997     case EL_NEXT_CE_7:
1998     case EL_NEXT_CE_8:
1999       // reference elements should not be used on the playfield
2000       Feld[x][y] = EL_EMPTY;
2001       break;
2002
2003     default:
2004       if (IS_CUSTOM_ELEMENT(element))
2005       {
2006         if (CAN_MOVE(element))
2007           InitMovDir(x, y);
2008
2009         if (!element_info[element].use_last_ce_value || init_game)
2010           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
2011       }
2012       else if (IS_GROUP_ELEMENT(element))
2013       {
2014         Feld[x][y] = GetElementFromGroupElement(element);
2015
2016         InitField(x, y, init_game);
2017       }
2018
2019       break;
2020   }
2021
2022   if (!init_game)
2023     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2024 }
2025
2026 static void InitField_WithBug1(int x, int y, boolean init_game)
2027 {
2028   InitField(x, y, init_game);
2029
2030   // not needed to call InitMovDir() -- already done by InitField()!
2031   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2032       CAN_MOVE(Feld[x][y]))
2033     InitMovDir(x, y);
2034 }
2035
2036 static void InitField_WithBug2(int x, int y, boolean init_game)
2037 {
2038   int old_element = Feld[x][y];
2039
2040   InitField(x, y, init_game);
2041
2042   // not needed to call InitMovDir() -- already done by InitField()!
2043   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2044       CAN_MOVE(old_element) &&
2045       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2046     InitMovDir(x, y);
2047
2048   /* this case is in fact a combination of not less than three bugs:
2049      first, it calls InitMovDir() for elements that can move, although this is
2050      already done by InitField(); then, it checks the element that was at this
2051      field _before_ the call to InitField() (which can change it); lastly, it
2052      was not called for "mole with direction" elements, which were treated as
2053      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2054   */
2055 }
2056
2057 static int get_key_element_from_nr(int key_nr)
2058 {
2059   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2060                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2061                           EL_EM_KEY_1 : EL_KEY_1);
2062
2063   return key_base_element + key_nr;
2064 }
2065
2066 static int get_next_dropped_element(struct PlayerInfo *player)
2067 {
2068   return (player->inventory_size > 0 ?
2069           player->inventory_element[player->inventory_size - 1] :
2070           player->inventory_infinite_element != EL_UNDEFINED ?
2071           player->inventory_infinite_element :
2072           player->dynabombs_left > 0 ?
2073           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2074           EL_UNDEFINED);
2075 }
2076
2077 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2078 {
2079   // pos >= 0: get element from bottom of the stack;
2080   // pos <  0: get element from top of the stack
2081
2082   if (pos < 0)
2083   {
2084     int min_inventory_size = -pos;
2085     int inventory_pos = player->inventory_size - min_inventory_size;
2086     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2087
2088     return (player->inventory_size >= min_inventory_size ?
2089             player->inventory_element[inventory_pos] :
2090             player->inventory_infinite_element != EL_UNDEFINED ?
2091             player->inventory_infinite_element :
2092             player->dynabombs_left >= min_dynabombs_left ?
2093             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2094             EL_UNDEFINED);
2095   }
2096   else
2097   {
2098     int min_dynabombs_left = pos + 1;
2099     int min_inventory_size = pos + 1 - player->dynabombs_left;
2100     int inventory_pos = pos - player->dynabombs_left;
2101
2102     return (player->inventory_infinite_element != EL_UNDEFINED ?
2103             player->inventory_infinite_element :
2104             player->dynabombs_left >= min_dynabombs_left ?
2105             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2106             player->inventory_size >= min_inventory_size ?
2107             player->inventory_element[inventory_pos] :
2108             EL_UNDEFINED);
2109   }
2110 }
2111
2112 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2113 {
2114   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2115   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2116   int compare_result;
2117
2118   if (gpo1->sort_priority != gpo2->sort_priority)
2119     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2120   else
2121     compare_result = gpo1->nr - gpo2->nr;
2122
2123   return compare_result;
2124 }
2125
2126 int getPlayerInventorySize(int player_nr)
2127 {
2128   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2129     return game_em.ply[player_nr]->dynamite;
2130   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2131     return game_sp.red_disk_count;
2132   else
2133     return stored_player[player_nr].inventory_size;
2134 }
2135
2136 static void InitGameControlValues(void)
2137 {
2138   int i;
2139
2140   for (i = 0; game_panel_controls[i].nr != -1; i++)
2141   {
2142     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2143     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2144     struct TextPosInfo *pos = gpc->pos;
2145     int nr = gpc->nr;
2146     int type = gpc->type;
2147
2148     if (nr != i)
2149     {
2150       Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2151       Error(ERR_EXIT, "this should not happen -- please debug");
2152     }
2153
2154     // force update of game controls after initialization
2155     gpc->value = gpc->last_value = -1;
2156     gpc->frame = gpc->last_frame = -1;
2157     gpc->gfx_frame = -1;
2158
2159     // determine panel value width for later calculation of alignment
2160     if (type == TYPE_INTEGER || type == TYPE_STRING)
2161     {
2162       pos->width = pos->size * getFontWidth(pos->font);
2163       pos->height = getFontHeight(pos->font);
2164     }
2165     else if (type == TYPE_ELEMENT)
2166     {
2167       pos->width = pos->size;
2168       pos->height = pos->size;
2169     }
2170
2171     // fill structure for game panel draw order
2172     gpo->nr = gpc->nr;
2173     gpo->sort_priority = pos->sort_priority;
2174   }
2175
2176   // sort game panel controls according to sort_priority and control number
2177   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2178         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2179 }
2180
2181 static void UpdatePlayfieldElementCount(void)
2182 {
2183   boolean use_element_count = FALSE;
2184   int i, j, x, y;
2185
2186   // first check if it is needed at all to calculate playfield element count
2187   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2188     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2189       use_element_count = TRUE;
2190
2191   if (!use_element_count)
2192     return;
2193
2194   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2195     element_info[i].element_count = 0;
2196
2197   SCAN_PLAYFIELD(x, y)
2198   {
2199     element_info[Feld[x][y]].element_count++;
2200   }
2201
2202   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2203     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2204       if (IS_IN_GROUP(j, i))
2205         element_info[EL_GROUP_START + i].element_count +=
2206           element_info[j].element_count;
2207 }
2208
2209 static void UpdateGameControlValues(void)
2210 {
2211   int i, k;
2212   int time = (game.LevelSolved ?
2213               game.LevelSolved_CountingTime :
2214               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2215               game_em.lev->time :
2216               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2217               game_sp.time_played :
2218               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2219               game_mm.energy_left :
2220               game.no_time_limit ? TimePlayed : TimeLeft);
2221   int score = (game.LevelSolved ?
2222                game.LevelSolved_CountingScore :
2223                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2224                game_em.lev->score :
2225                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2226                game_sp.score :
2227                level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2228                game_mm.score :
2229                game.score);
2230   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2231               game_em.lev->gems_needed :
2232               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2233               game_sp.infotrons_still_needed :
2234               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2235               game_mm.kettles_still_needed :
2236               game.gems_still_needed);
2237   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2238                      game_em.lev->gems_needed > 0 :
2239                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2240                      game_sp.infotrons_still_needed > 0 :
2241                      level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2242                      game_mm.kettles_still_needed > 0 ||
2243                      game_mm.lights_still_needed > 0 :
2244                      game.gems_still_needed > 0 ||
2245                      game.sokoban_fields_still_needed > 0 ||
2246                      game.sokoban_objects_still_needed > 0 ||
2247                      game.lights_still_needed > 0);
2248   int health = (game.LevelSolved ?
2249                 game.LevelSolved_CountingHealth :
2250                 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2251                 MM_HEALTH(game_mm.laser_overload_value) :
2252                 game.health);
2253
2254   UpdatePlayfieldElementCount();
2255
2256   // update game panel control values
2257
2258   // used instead of "level_nr" (for network games)
2259   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = levelset.level_nr;
2260   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2261
2262   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2263   for (i = 0; i < MAX_NUM_KEYS; i++)
2264     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2265   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2266   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2267
2268   if (game.centered_player_nr == -1)
2269   {
2270     for (i = 0; i < MAX_PLAYERS; i++)
2271     {
2272       // only one player in Supaplex game engine
2273       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2274         break;
2275
2276       for (k = 0; k < MAX_NUM_KEYS; k++)
2277       {
2278         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2279         {
2280           if (game_em.ply[i]->keys & (1 << k))
2281             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2282               get_key_element_from_nr(k);
2283         }
2284         else if (stored_player[i].key[k])
2285           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2286             get_key_element_from_nr(k);
2287       }
2288
2289       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2290         getPlayerInventorySize(i);
2291
2292       if (stored_player[i].num_white_keys > 0)
2293         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2294           EL_DC_KEY_WHITE;
2295
2296       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2297         stored_player[i].num_white_keys;
2298     }
2299   }
2300   else
2301   {
2302     int player_nr = game.centered_player_nr;
2303
2304     for (k = 0; k < MAX_NUM_KEYS; k++)
2305     {
2306       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2307       {
2308         if (game_em.ply[player_nr]->keys & (1 << k))
2309           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2310             get_key_element_from_nr(k);
2311       }
2312       else if (stored_player[player_nr].key[k])
2313         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2314           get_key_element_from_nr(k);
2315     }
2316
2317     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2318       getPlayerInventorySize(player_nr);
2319
2320     if (stored_player[player_nr].num_white_keys > 0)
2321       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2322
2323     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2324       stored_player[player_nr].num_white_keys;
2325   }
2326
2327   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2328   {
2329     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2330       get_inventory_element_from_pos(local_player, i);
2331     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2332       get_inventory_element_from_pos(local_player, -i - 1);
2333   }
2334
2335   game_panel_controls[GAME_PANEL_SCORE].value = score;
2336   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2337
2338   game_panel_controls[GAME_PANEL_TIME].value = time;
2339
2340   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2341   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2342   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2343
2344   if (level.time == 0)
2345     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2346   else
2347     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2348
2349   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2350   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2351
2352   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2353
2354   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2355     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2356      EL_EMPTY);
2357   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2358     local_player->shield_normal_time_left;
2359   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2360     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2361      EL_EMPTY);
2362   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2363     local_player->shield_deadly_time_left;
2364
2365   game_panel_controls[GAME_PANEL_EXIT].value =
2366     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2367
2368   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2369     (game.ball_active ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2370   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2371     (game.ball_active ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2372      EL_EMC_MAGIC_BALL_SWITCH);
2373
2374   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2375     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2376   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2377     game.light_time_left;
2378
2379   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2380     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2381   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2382     game.timegate_time_left;
2383
2384   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2385     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2386
2387   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2388     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2389   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2390     game.lenses_time_left;
2391
2392   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2393     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2394   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2395     game.magnify_time_left;
2396
2397   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2398     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2399      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2400      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2401      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2402      EL_BALLOON_SWITCH_NONE);
2403
2404   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2405     local_player->dynabomb_count;
2406   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2407     local_player->dynabomb_size;
2408   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2409     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2410
2411   game_panel_controls[GAME_PANEL_PENGUINS].value =
2412     game.friends_still_needed;
2413
2414   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2415     game.sokoban_objects_still_needed;
2416   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2417     game.sokoban_fields_still_needed;
2418
2419   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2420     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2421
2422   for (i = 0; i < NUM_BELTS; i++)
2423   {
2424     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2425       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2426        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2427     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2428       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2429   }
2430
2431   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2432     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2433   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2434     game.magic_wall_time_left;
2435
2436   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2437     local_player->gravity;
2438
2439   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2440     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2441
2442   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2443     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2444       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2445        game.panel.element[i].id : EL_UNDEFINED);
2446
2447   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2448     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2449       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2450        element_info[game.panel.element_count[i].id].element_count : 0);
2451
2452   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2453     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2454       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2455        element_info[game.panel.ce_score[i].id].collect_score : 0);
2456
2457   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2458     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2459       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2460        element_info[game.panel.ce_score_element[i].id].collect_score :
2461        EL_UNDEFINED);
2462
2463   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2464   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2465   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2466
2467   // update game panel control frames
2468
2469   for (i = 0; game_panel_controls[i].nr != -1; i++)
2470   {
2471     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2472
2473     if (gpc->type == TYPE_ELEMENT)
2474     {
2475       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2476       {
2477         int last_anim_random_frame = gfx.anim_random_frame;
2478         int element = gpc->value;
2479         int graphic = el2panelimg(element);
2480
2481         if (gpc->value != gpc->last_value)
2482         {
2483           gpc->gfx_frame = 0;
2484           gpc->gfx_random = INIT_GFX_RANDOM();
2485         }
2486         else
2487         {
2488           gpc->gfx_frame++;
2489
2490           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2491               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2492             gpc->gfx_random = INIT_GFX_RANDOM();
2493         }
2494
2495         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2496           gfx.anim_random_frame = gpc->gfx_random;
2497
2498         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2499           gpc->gfx_frame = element_info[element].collect_score;
2500
2501         gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2502                                               gpc->gfx_frame);
2503
2504         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2505           gfx.anim_random_frame = last_anim_random_frame;
2506       }
2507     }
2508     else if (gpc->type == TYPE_GRAPHIC)
2509     {
2510       if (gpc->graphic != IMG_UNDEFINED)
2511       {
2512         int last_anim_random_frame = gfx.anim_random_frame;
2513         int graphic = gpc->graphic;
2514
2515         if (gpc->value != gpc->last_value)
2516         {
2517           gpc->gfx_frame = 0;
2518           gpc->gfx_random = INIT_GFX_RANDOM();
2519         }
2520         else
2521         {
2522           gpc->gfx_frame++;
2523
2524           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2525               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2526             gpc->gfx_random = INIT_GFX_RANDOM();
2527         }
2528
2529         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2530           gfx.anim_random_frame = gpc->gfx_random;
2531
2532         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2533
2534         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2535           gfx.anim_random_frame = last_anim_random_frame;
2536       }
2537     }
2538   }
2539 }
2540
2541 static void DisplayGameControlValues(void)
2542 {
2543   boolean redraw_panel = FALSE;
2544   int i;
2545
2546   for (i = 0; game_panel_controls[i].nr != -1; i++)
2547   {
2548     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2549
2550     if (PANEL_DEACTIVATED(gpc->pos))
2551       continue;
2552
2553     if (gpc->value == gpc->last_value &&
2554         gpc->frame == gpc->last_frame)
2555       continue;
2556
2557     redraw_panel = TRUE;
2558   }
2559
2560   if (!redraw_panel)
2561     return;
2562
2563   // copy default game door content to main double buffer
2564
2565   // !!! CHECK AGAIN !!!
2566   SetPanelBackground();
2567   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2568   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2569
2570   // redraw game control buttons
2571   RedrawGameButtons();
2572
2573   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2574
2575   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2576   {
2577     int nr = game_panel_order[i].nr;
2578     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2579     struct TextPosInfo *pos = gpc->pos;
2580     int type = gpc->type;
2581     int value = gpc->value;
2582     int frame = gpc->frame;
2583     int size = pos->size;
2584     int font = pos->font;
2585     boolean draw_masked = pos->draw_masked;
2586     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2587
2588     if (PANEL_DEACTIVATED(pos))
2589       continue;
2590
2591     gpc->last_value = value;
2592     gpc->last_frame = frame;
2593
2594     if (type == TYPE_INTEGER)
2595     {
2596       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2597           nr == GAME_PANEL_TIME)
2598       {
2599         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2600
2601         if (use_dynamic_size)           // use dynamic number of digits
2602         {
2603           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2604           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2605           int size2 = size1 + 1;
2606           int font1 = pos->font;
2607           int font2 = pos->font_alt;
2608
2609           size = (value < value_change ? size1 : size2);
2610           font = (value < value_change ? font1 : font2);
2611         }
2612       }
2613
2614       // correct text size if "digits" is zero or less
2615       if (size <= 0)
2616         size = strlen(int2str(value, size));
2617
2618       // dynamically correct text alignment
2619       pos->width = size * getFontWidth(font);
2620
2621       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2622                   int2str(value, size), font, mask_mode);
2623     }
2624     else if (type == TYPE_ELEMENT)
2625     {
2626       int element, graphic;
2627       Bitmap *src_bitmap;
2628       int src_x, src_y;
2629       int width, height;
2630       int dst_x = PANEL_XPOS(pos);
2631       int dst_y = PANEL_YPOS(pos);
2632
2633       if (value != EL_UNDEFINED && value != EL_EMPTY)
2634       {
2635         element = value;
2636         graphic = el2panelimg(value);
2637
2638         // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2639
2640         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2641           size = TILESIZE;
2642
2643         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2644                               &src_x, &src_y);
2645
2646         width  = graphic_info[graphic].width  * size / TILESIZE;
2647         height = graphic_info[graphic].height * size / TILESIZE;
2648
2649         if (draw_masked)
2650           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2651                            dst_x, dst_y);
2652         else
2653           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2654                      dst_x, dst_y);
2655       }
2656     }
2657     else if (type == TYPE_GRAPHIC)
2658     {
2659       int graphic        = gpc->graphic;
2660       int graphic_active = gpc->graphic_active;
2661       Bitmap *src_bitmap;
2662       int src_x, src_y;
2663       int width, height;
2664       int dst_x = PANEL_XPOS(pos);
2665       int dst_y = PANEL_YPOS(pos);
2666       boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2667                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2668
2669       if (graphic != IMG_UNDEFINED && !skip)
2670       {
2671         if (pos->style == STYLE_REVERSE)
2672           value = 100 - value;
2673
2674         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2675
2676         if (pos->direction & MV_HORIZONTAL)
2677         {
2678           width  = graphic_info[graphic_active].width * value / 100;
2679           height = graphic_info[graphic_active].height;
2680
2681           if (pos->direction == MV_LEFT)
2682           {
2683             src_x += graphic_info[graphic_active].width - width;
2684             dst_x += graphic_info[graphic_active].width - width;
2685           }
2686         }
2687         else
2688         {
2689           width  = graphic_info[graphic_active].width;
2690           height = graphic_info[graphic_active].height * value / 100;
2691
2692           if (pos->direction == MV_UP)
2693           {
2694             src_y += graphic_info[graphic_active].height - height;
2695             dst_y += graphic_info[graphic_active].height - height;
2696           }
2697         }
2698
2699         if (draw_masked)
2700           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2701                            dst_x, dst_y);
2702         else
2703           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2704                      dst_x, dst_y);
2705
2706         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2707
2708         if (pos->direction & MV_HORIZONTAL)
2709         {
2710           if (pos->direction == MV_RIGHT)
2711           {
2712             src_x += width;
2713             dst_x += width;
2714           }
2715           else
2716           {
2717             dst_x = PANEL_XPOS(pos);
2718           }
2719
2720           width = graphic_info[graphic].width - width;
2721         }
2722         else
2723         {
2724           if (pos->direction == MV_DOWN)
2725           {
2726             src_y += height;
2727             dst_y += height;
2728           }
2729           else
2730           {
2731             dst_y = PANEL_YPOS(pos);
2732           }
2733
2734           height = graphic_info[graphic].height - height;
2735         }
2736
2737         if (draw_masked)
2738           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2739                            dst_x, dst_y);
2740         else
2741           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2742                      dst_x, dst_y);
2743       }
2744     }
2745     else if (type == TYPE_STRING)
2746     {
2747       boolean active = (value != 0);
2748       char *state_normal = "off";
2749       char *state_active = "on";
2750       char *state = (active ? state_active : state_normal);
2751       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2752                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2753                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2754                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2755
2756       if (nr == GAME_PANEL_GRAVITY_STATE)
2757       {
2758         int font1 = pos->font;          // (used for normal state)
2759         int font2 = pos->font_alt;      // (used for active state)
2760
2761         font = (active ? font2 : font1);
2762       }
2763
2764       if (s != NULL)
2765       {
2766         char *s_cut;
2767
2768         if (size <= 0)
2769         {
2770           // don't truncate output if "chars" is zero or less
2771           size = strlen(s);
2772
2773           // dynamically correct text alignment
2774           pos->width = size * getFontWidth(font);
2775         }
2776
2777         s_cut = getStringCopyN(s, size);
2778
2779         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2780                     s_cut, font, mask_mode);
2781
2782         free(s_cut);
2783       }
2784     }
2785
2786     redraw_mask |= REDRAW_DOOR_1;
2787   }
2788
2789   SetGameStatus(GAME_MODE_PLAYING);
2790 }
2791
2792 void UpdateAndDisplayGameControlValues(void)
2793 {
2794   if (tape.deactivate_display)
2795     return;
2796
2797   UpdateGameControlValues();
2798   DisplayGameControlValues();
2799 }
2800
2801 #if 0
2802 static void UpdateGameDoorValues(void)
2803 {
2804   UpdateGameControlValues();
2805 }
2806 #endif
2807
2808 void DrawGameDoorValues(void)
2809 {
2810   DisplayGameControlValues();
2811 }
2812
2813
2814 // ============================================================================
2815 // InitGameEngine()
2816 // ----------------------------------------------------------------------------
2817 // initialize game engine due to level / tape version number
2818 // ============================================================================
2819
2820 static void InitGameEngine(void)
2821 {
2822   int i, j, k, l, x, y;
2823
2824   // set game engine from tape file when re-playing, else from level file
2825   game.engine_version = (tape.playing ? tape.engine_version :
2826                          level.game_version);
2827
2828   // set single or multi-player game mode (needed for re-playing tapes)
2829   game.team_mode = setup.team_mode;
2830
2831   if (tape.playing)
2832   {
2833     int num_players = 0;
2834
2835     for (i = 0; i < MAX_PLAYERS; i++)
2836       if (tape.player_participates[i])
2837         num_players++;
2838
2839     // multi-player tapes contain input data for more than one player
2840     game.team_mode = (num_players > 1);
2841   }
2842
2843   // --------------------------------------------------------------------------
2844   // set flags for bugs and changes according to active game engine version
2845   // --------------------------------------------------------------------------
2846
2847   /*
2848     Summary of bugfix/change:
2849     Fixed handling for custom elements that change when pushed by the player.
2850
2851     Fixed/changed in version:
2852     3.1.0
2853
2854     Description:
2855     Before 3.1.0, custom elements that "change when pushing" changed directly
2856     after the player started pushing them (until then handled in "DigField()").
2857     Since 3.1.0, these custom elements are not changed until the "pushing"
2858     move of the element is finished (now handled in "ContinueMoving()").
2859
2860     Affected levels/tapes:
2861     The first condition is generally needed for all levels/tapes before version
2862     3.1.0, which might use the old behaviour before it was changed; known tapes
2863     that are affected are some tapes from the level set "Walpurgis Gardens" by
2864     Jamie Cullen.
2865     The second condition is an exception from the above case and is needed for
2866     the special case of tapes recorded with game (not engine!) version 3.1.0 or
2867     above (including some development versions of 3.1.0), but before it was
2868     known that this change would break tapes like the above and was fixed in
2869     3.1.1, so that the changed behaviour was active although the engine version
2870     while recording maybe was before 3.1.0. There is at least one tape that is
2871     affected by this exception, which is the tape for the one-level set "Bug
2872     Machine" by Juergen Bonhagen.
2873   */
2874
2875   game.use_change_when_pushing_bug =
2876     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2877      !(tape.playing &&
2878        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2879        tape.game_version <  VERSION_IDENT(3,1,1,0)));
2880
2881   /*
2882     Summary of bugfix/change:
2883     Fixed handling for blocking the field the player leaves when moving.
2884
2885     Fixed/changed in version:
2886     3.1.1
2887
2888     Description:
2889     Before 3.1.1, when "block last field when moving" was enabled, the field
2890     the player is leaving when moving was blocked for the time of the move,
2891     and was directly unblocked afterwards. This resulted in the last field
2892     being blocked for exactly one less than the number of frames of one player
2893     move. Additionally, even when blocking was disabled, the last field was
2894     blocked for exactly one frame.
2895     Since 3.1.1, due to changes in player movement handling, the last field
2896     is not blocked at all when blocking is disabled. When blocking is enabled,
2897     the last field is blocked for exactly the number of frames of one player
2898     move. Additionally, if the player is Murphy, the hero of Supaplex, the
2899     last field is blocked for exactly one more than the number of frames of
2900     one player move.
2901
2902     Affected levels/tapes:
2903     (!!! yet to be determined -- probably many !!!)
2904   */
2905
2906   game.use_block_last_field_bug =
2907     (game.engine_version < VERSION_IDENT(3,1,1,0));
2908
2909   /* various special flags and settings for native Emerald Mine game engine */
2910
2911   game_em.use_single_button =
2912     (game.engine_version > VERSION_IDENT(4,0,0,2));
2913
2914   game_em.use_snap_key_bug =
2915     (game.engine_version < VERSION_IDENT(4,0,1,0));
2916
2917   game_em.use_old_explosions =
2918     (game.engine_version < VERSION_IDENT(4,1,4,2));
2919
2920   // --------------------------------------------------------------------------
2921
2922   // set maximal allowed number of custom element changes per game frame
2923   game.max_num_changes_per_frame = 1;
2924
2925   // default scan direction: scan playfield from top/left to bottom/right
2926   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2927
2928   // dynamically adjust element properties according to game engine version
2929   InitElementPropertiesEngine(game.engine_version);
2930
2931 #if 0
2932   printf("level %d: level version == %06d\n", level_nr, level.game_version);
2933   printf("          tape version == %06d [%s] [file: %06d]\n",
2934          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2935          tape.file_version);
2936   printf("       => game.engine_version == %06d\n", game.engine_version);
2937 #endif
2938
2939   // ---------- initialize player's initial move delay ------------------------
2940
2941   // dynamically adjust player properties according to level information
2942   for (i = 0; i < MAX_PLAYERS; i++)
2943     game.initial_move_delay_value[i] =
2944       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2945
2946   // dynamically adjust player properties according to game engine version
2947   for (i = 0; i < MAX_PLAYERS; i++)
2948     game.initial_move_delay[i] =
2949       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2950        game.initial_move_delay_value[i] : 0);
2951
2952   // ---------- initialize player's initial push delay ------------------------
2953
2954   // dynamically adjust player properties according to game engine version
2955   game.initial_push_delay_value =
2956     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2957
2958   // ---------- initialize changing elements ----------------------------------
2959
2960   // initialize changing elements information
2961   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2962   {
2963     struct ElementInfo *ei = &element_info[i];
2964
2965     // this pointer might have been changed in the level editor
2966     ei->change = &ei->change_page[0];
2967
2968     if (!IS_CUSTOM_ELEMENT(i))
2969     {
2970       ei->change->target_element = EL_EMPTY_SPACE;
2971       ei->change->delay_fixed = 0;
2972       ei->change->delay_random = 0;
2973       ei->change->delay_frames = 1;
2974     }
2975
2976     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2977     {
2978       ei->has_change_event[j] = FALSE;
2979
2980       ei->event_page_nr[j] = 0;
2981       ei->event_page[j] = &ei->change_page[0];
2982     }
2983   }
2984
2985   // add changing elements from pre-defined list
2986   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2987   {
2988     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2989     struct ElementInfo *ei = &element_info[ch_delay->element];
2990
2991     ei->change->target_element       = ch_delay->target_element;
2992     ei->change->delay_fixed          = ch_delay->change_delay;
2993
2994     ei->change->pre_change_function  = ch_delay->pre_change_function;
2995     ei->change->change_function      = ch_delay->change_function;
2996     ei->change->post_change_function = ch_delay->post_change_function;
2997
2998     ei->change->can_change = TRUE;
2999     ei->change->can_change_or_has_action = TRUE;
3000
3001     ei->has_change_event[CE_DELAY] = TRUE;
3002
3003     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3004     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3005   }
3006
3007   // ---------- initialize internal run-time variables ------------------------
3008
3009   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3010   {
3011     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3012
3013     for (j = 0; j < ei->num_change_pages; j++)
3014     {
3015       ei->change_page[j].can_change_or_has_action =
3016         (ei->change_page[j].can_change |
3017          ei->change_page[j].has_action);
3018     }
3019   }
3020
3021   // add change events from custom element configuration
3022   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3023   {
3024     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3025
3026     for (j = 0; j < ei->num_change_pages; j++)
3027     {
3028       if (!ei->change_page[j].can_change_or_has_action)
3029         continue;
3030
3031       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3032       {
3033         // only add event page for the first page found with this event
3034         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3035         {
3036           ei->has_change_event[k] = TRUE;
3037
3038           ei->event_page_nr[k] = j;
3039           ei->event_page[k] = &ei->change_page[j];
3040         }
3041       }
3042     }
3043   }
3044
3045   // ---------- initialize reference elements in change conditions ------------
3046
3047   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3048   {
3049     int element = EL_CUSTOM_START + i;
3050     struct ElementInfo *ei = &element_info[element];
3051
3052     for (j = 0; j < ei->num_change_pages; j++)
3053     {
3054       int trigger_element = ei->change_page[j].initial_trigger_element;
3055
3056       if (trigger_element >= EL_PREV_CE_8 &&
3057           trigger_element <= EL_NEXT_CE_8)
3058         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3059
3060       ei->change_page[j].trigger_element = trigger_element;
3061     }
3062   }
3063
3064   // ---------- initialize run-time trigger player and element ----------------
3065
3066   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3067   {
3068     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3069
3070     for (j = 0; j < ei->num_change_pages; j++)
3071     {
3072       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3073       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3074       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3075       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3076       ei->change_page[j].actual_trigger_ce_value = 0;
3077       ei->change_page[j].actual_trigger_ce_score = 0;
3078     }
3079   }
3080
3081   // ---------- initialize trigger events -------------------------------------
3082
3083   // initialize trigger events information
3084   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3085     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3086       trigger_events[i][j] = FALSE;
3087
3088   // add trigger events from element change event properties
3089   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3090   {
3091     struct ElementInfo *ei = &element_info[i];
3092
3093     for (j = 0; j < ei->num_change_pages; j++)
3094     {
3095       if (!ei->change_page[j].can_change_or_has_action)
3096         continue;
3097
3098       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3099       {
3100         int trigger_element = ei->change_page[j].trigger_element;
3101
3102         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3103         {
3104           if (ei->change_page[j].has_event[k])
3105           {
3106             if (IS_GROUP_ELEMENT(trigger_element))
3107             {
3108               struct ElementGroupInfo *group =
3109                 element_info[trigger_element].group;
3110
3111               for (l = 0; l < group->num_elements_resolved; l++)
3112                 trigger_events[group->element_resolved[l]][k] = TRUE;
3113             }
3114             else if (trigger_element == EL_ANY_ELEMENT)
3115               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3116                 trigger_events[l][k] = TRUE;
3117             else
3118               trigger_events[trigger_element][k] = TRUE;
3119           }
3120         }
3121       }
3122     }
3123   }
3124
3125   // ---------- initialize push delay -----------------------------------------
3126
3127   // initialize push delay values to default
3128   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3129   {
3130     if (!IS_CUSTOM_ELEMENT(i))
3131     {
3132       // set default push delay values (corrected since version 3.0.7-1)
3133       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3134       {
3135         element_info[i].push_delay_fixed = 2;
3136         element_info[i].push_delay_random = 8;
3137       }
3138       else
3139       {
3140         element_info[i].push_delay_fixed = 8;
3141         element_info[i].push_delay_random = 8;
3142       }
3143     }
3144   }
3145
3146   // set push delay value for certain elements from pre-defined list
3147   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3148   {
3149     int e = push_delay_list[i].element;
3150
3151     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3152     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3153   }
3154
3155   // set push delay value for Supaplex elements for newer engine versions
3156   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3157   {
3158     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3159     {
3160       if (IS_SP_ELEMENT(i))
3161       {
3162         // set SP push delay to just enough to push under a falling zonk
3163         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3164
3165         element_info[i].push_delay_fixed  = delay;
3166         element_info[i].push_delay_random = 0;
3167       }
3168     }
3169   }
3170
3171   // ---------- initialize move stepsize --------------------------------------
3172
3173   // initialize move stepsize values to default
3174   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3175     if (!IS_CUSTOM_ELEMENT(i))
3176       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3177
3178   // set move stepsize value for certain elements from pre-defined list
3179   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3180   {
3181     int e = move_stepsize_list[i].element;
3182
3183     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3184   }
3185
3186   // ---------- initialize collect score --------------------------------------
3187
3188   // initialize collect score values for custom elements from initial value
3189   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3190     if (IS_CUSTOM_ELEMENT(i))
3191       element_info[i].collect_score = element_info[i].collect_score_initial;
3192
3193   // ---------- initialize collect count --------------------------------------
3194
3195   // initialize collect count values for non-custom elements
3196   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3197     if (!IS_CUSTOM_ELEMENT(i))
3198       element_info[i].collect_count_initial = 0;
3199
3200   // add collect count values for all elements from pre-defined list
3201   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3202     element_info[collect_count_list[i].element].collect_count_initial =
3203       collect_count_list[i].count;
3204
3205   // ---------- initialize access direction -----------------------------------
3206
3207   // initialize access direction values to default (access from every side)
3208   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3209     if (!IS_CUSTOM_ELEMENT(i))
3210       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3211
3212   // set access direction value for certain elements from pre-defined list
3213   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3214     element_info[access_direction_list[i].element].access_direction =
3215       access_direction_list[i].direction;
3216
3217   // ---------- initialize explosion content ----------------------------------
3218   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3219   {
3220     if (IS_CUSTOM_ELEMENT(i))
3221       continue;
3222
3223     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3224     {
3225       // (content for EL_YAMYAM set at run-time with game.yamyam_content_nr)
3226
3227       element_info[i].content.e[x][y] =
3228         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3229          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3230          i == EL_PLAYER_3 ? EL_EMERALD :
3231          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3232          i == EL_MOLE ? EL_EMERALD_RED :
3233          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3234          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3235          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3236          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3237          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3238          i == EL_WALL_EMERALD ? EL_EMERALD :
3239          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3240          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3241          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3242          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3243          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3244          i == EL_WALL_PEARL ? EL_PEARL :
3245          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3246          EL_EMPTY);
3247     }
3248   }
3249
3250   // ---------- initialize recursion detection --------------------------------
3251   recursion_loop_depth = 0;
3252   recursion_loop_detected = FALSE;
3253   recursion_loop_element = EL_UNDEFINED;
3254
3255   // ---------- initialize graphics engine ------------------------------------
3256   game.scroll_delay_value =
3257     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3258      level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3259      !setup.forced_scroll_delay           ? 0 :
3260      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3261   game.scroll_delay_value =
3262     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3263
3264   // ---------- initialize game engine snapshots ------------------------------
3265   for (i = 0; i < MAX_PLAYERS; i++)
3266     game.snapshot.last_action[i] = 0;
3267   game.snapshot.changed_action = FALSE;
3268   game.snapshot.collected_item = FALSE;
3269   game.snapshot.mode =
3270     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3271      SNAPSHOT_MODE_EVERY_STEP :
3272      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3273      SNAPSHOT_MODE_EVERY_MOVE :
3274      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3275      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3276   game.snapshot.save_snapshot = FALSE;
3277
3278   // ---------- initialize level time for Supaplex engine ---------------------
3279   // Supaplex levels with time limit currently unsupported -- should be added
3280   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3281     level.time = 0;
3282
3283   // ----------initialize flag for handling mouse events ---------------------
3284
3285   // set flag to default value: do not handle mouse events
3286   game.use_mouse_events = FALSE;
3287
3288   // now check for custom elements which have mouse click events defined
3289   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3290   {
3291     int element = EL_CUSTOM_START + i;
3292
3293     if (HAS_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
3294         HAS_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE) ||
3295         HAS_CHANGE_EVENT(element, CE_MOUSE_CLICKED_ON_X) ||
3296         HAS_CHANGE_EVENT(element, CE_MOUSE_PRESSED_ON_X))
3297       game.use_mouse_events = TRUE;
3298   }
3299 }
3300
3301 static int get_num_special_action(int element, int action_first,
3302                                   int action_last)
3303 {
3304   int num_special_action = 0;
3305   int i, j;
3306
3307   for (i = action_first; i <= action_last; i++)
3308   {
3309     boolean found = FALSE;
3310
3311     for (j = 0; j < NUM_DIRECTIONS; j++)
3312       if (el_act_dir2img(element, i, j) !=
3313           el_act_dir2img(element, ACTION_DEFAULT, j))
3314         found = TRUE;
3315
3316     if (found)
3317       num_special_action++;
3318     else
3319       break;
3320   }
3321
3322   return num_special_action;
3323 }
3324
3325
3326 // ============================================================================
3327 // InitGame()
3328 // ----------------------------------------------------------------------------
3329 // initialize and start new game
3330 // ============================================================================
3331
3332 #if DEBUG_INIT_PLAYER
3333 static void DebugPrintPlayerStatus(char *message)
3334 {
3335   int i;
3336
3337   if (!options.debug)
3338     return;
3339
3340   printf("%s:\n", message);
3341
3342   for (i = 0; i < MAX_PLAYERS; i++)
3343   {
3344     struct PlayerInfo *player = &stored_player[i];
3345
3346     printf("- player %d: present == %d, connected == %d [%d/%d], active == %d",
3347            i + 1,
3348            player->present,
3349            player->connected,
3350            player->connected_locally,
3351            player->connected_network,
3352            player->active);
3353
3354     if (local_player == player)
3355       printf(" (local player)");
3356
3357     printf("\n");
3358   }
3359 }
3360 #endif
3361
3362 void InitGame(void)
3363 {
3364   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3365   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3366   int fade_mask = REDRAW_FIELD;
3367
3368   boolean emulate_bd = TRUE;    // unless non-BOULDERDASH elements found
3369   boolean emulate_sb = TRUE;    // unless non-SOKOBAN     elements found
3370   boolean emulate_sp = TRUE;    // unless non-SUPAPLEX    elements found
3371   int initial_move_dir = MV_DOWN;
3372   int i, j, x, y;
3373
3374   // required here to update video display before fading (FIX THIS)
3375   DrawMaskedBorder(REDRAW_DOOR_2);
3376
3377   if (!game.restart_level)
3378     CloseDoor(DOOR_CLOSE_1);
3379
3380   SetGameStatus(GAME_MODE_PLAYING);
3381
3382   if (level_editor_test_game)
3383     FadeSkipNextFadeOut();
3384   else
3385     FadeSetEnterScreen();
3386
3387   if (CheckFadeAll())
3388     fade_mask = REDRAW_ALL;
3389
3390   FadeLevelSoundsAndMusic();
3391
3392   ExpireSoundLoops(TRUE);
3393
3394   FadeOut(fade_mask);
3395
3396   if (level_editor_test_game)
3397     FadeSkipNextFadeIn();
3398
3399   // needed if different viewport properties defined for playing
3400   ChangeViewportPropertiesIfNeeded();
3401
3402   ClearField();
3403
3404   DrawCompleteVideoDisplay();
3405
3406   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3407
3408   InitGameEngine();
3409   InitGameControlValues();
3410
3411   // don't play tapes over network
3412   network_playing = (network.enabled && !tape.playing);
3413
3414   for (i = 0; i < MAX_PLAYERS; i++)
3415   {
3416     struct PlayerInfo *player = &stored_player[i];
3417
3418     player->index_nr = i;
3419     player->index_bit = (1 << i);
3420     player->element_nr = EL_PLAYER_1 + i;
3421
3422     player->present = FALSE;
3423     player->active = FALSE;
3424     player->mapped = FALSE;
3425
3426     player->killed = FALSE;
3427     player->reanimated = FALSE;
3428     player->buried = FALSE;
3429
3430     player->action = 0;
3431     player->effective_action = 0;
3432     player->programmed_action = 0;
3433     player->snap_action = 0;
3434
3435     player->mouse_action.lx = 0;
3436     player->mouse_action.ly = 0;
3437     player->mouse_action.button = 0;
3438     player->mouse_action.button_hint = 0;
3439
3440     player->effective_mouse_action.lx = 0;
3441     player->effective_mouse_action.ly = 0;
3442     player->effective_mouse_action.button = 0;
3443     player->effective_mouse_action.button_hint = 0;
3444
3445     for (j = 0; j < MAX_NUM_KEYS; j++)
3446       player->key[j] = FALSE;
3447
3448     player->num_white_keys = 0;
3449
3450     player->dynabomb_count = 0;
3451     player->dynabomb_size = 1;
3452     player->dynabombs_left = 0;
3453     player->dynabomb_xl = FALSE;
3454
3455     player->MovDir = initial_move_dir;
3456     player->MovPos = 0;
3457     player->GfxPos = 0;
3458     player->GfxDir = initial_move_dir;
3459     player->GfxAction = ACTION_DEFAULT;
3460     player->Frame = 0;
3461     player->StepFrame = 0;
3462
3463     player->initial_element = player->element_nr;
3464     player->artwork_element =
3465       (level.use_artwork_element[i] ? level.artwork_element[i] :
3466        player->element_nr);
3467     player->use_murphy = FALSE;
3468
3469     player->block_last_field = FALSE;   // initialized in InitPlayerField()
3470     player->block_delay_adjustment = 0; // initialized in InitPlayerField()
3471
3472     player->gravity = level.initial_player_gravity[i];
3473
3474     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3475
3476     player->actual_frame_counter = 0;
3477
3478     player->step_counter = 0;
3479
3480     player->last_move_dir = initial_move_dir;
3481
3482     player->is_active = FALSE;
3483
3484     player->is_waiting = FALSE;
3485     player->is_moving = FALSE;
3486     player->is_auto_moving = FALSE;
3487     player->is_digging = FALSE;
3488     player->is_snapping = FALSE;
3489     player->is_collecting = FALSE;
3490     player->is_pushing = FALSE;
3491     player->is_switching = FALSE;
3492     player->is_dropping = FALSE;
3493     player->is_dropping_pressed = FALSE;
3494
3495     player->is_bored = FALSE;
3496     player->is_sleeping = FALSE;
3497
3498     player->was_waiting = TRUE;
3499     player->was_moving = FALSE;
3500     player->was_snapping = FALSE;
3501     player->was_dropping = FALSE;
3502
3503     player->force_dropping = FALSE;
3504
3505     player->frame_counter_bored = -1;
3506     player->frame_counter_sleeping = -1;
3507
3508     player->anim_delay_counter = 0;
3509     player->post_delay_counter = 0;
3510
3511     player->dir_waiting = initial_move_dir;
3512     player->action_waiting = ACTION_DEFAULT;
3513     player->last_action_waiting = ACTION_DEFAULT;
3514     player->special_action_bored = ACTION_DEFAULT;
3515     player->special_action_sleeping = ACTION_DEFAULT;
3516
3517     player->switch_x = -1;
3518     player->switch_y = -1;
3519
3520     player->drop_x = -1;
3521     player->drop_y = -1;
3522
3523     player->show_envelope = 0;
3524
3525     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3526
3527     player->push_delay       = -1;      // initialized when pushing starts
3528     player->push_delay_value = game.initial_push_delay_value;
3529
3530     player->drop_delay = 0;
3531     player->drop_pressed_delay = 0;
3532
3533     player->last_jx = -1;
3534     player->last_jy = -1;
3535     player->jx = -1;
3536     player->jy = -1;
3537
3538     player->shield_normal_time_left = 0;
3539     player->shield_deadly_time_left = 0;
3540
3541     player->inventory_infinite_element = EL_UNDEFINED;
3542     player->inventory_size = 0;
3543
3544     if (level.use_initial_inventory[i])
3545     {
3546       for (j = 0; j < level.initial_inventory_size[i]; j++)
3547       {
3548         int element = level.initial_inventory_content[i][j];
3549         int collect_count = element_info[element].collect_count_initial;
3550         int k;
3551
3552         if (!IS_CUSTOM_ELEMENT(element))
3553           collect_count = 1;
3554
3555         if (collect_count == 0)
3556           player->inventory_infinite_element = element;
3557         else
3558           for (k = 0; k < collect_count; k++)
3559             if (player->inventory_size < MAX_INVENTORY_SIZE)
3560               player->inventory_element[player->inventory_size++] = element;
3561       }
3562     }
3563
3564     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3565     SnapField(player, 0, 0);
3566
3567     map_player_action[i] = i;
3568   }
3569
3570   network_player_action_received = FALSE;
3571
3572   // initial null action
3573   if (network_playing)
3574     SendToServer_MovePlayer(MV_NONE);
3575
3576   FrameCounter = 0;
3577   TimeFrames = 0;
3578   TimePlayed = 0;
3579   TimeLeft = level.time;
3580   TapeTime = 0;
3581
3582   ScreenMovDir = MV_NONE;
3583   ScreenMovPos = 0;
3584   ScreenGfxPos = 0;
3585
3586   ScrollStepSize = 0;   // will be correctly initialized by ScrollScreen()
3587
3588   game.robot_wheel_x = -1;
3589   game.robot_wheel_y = -1;
3590
3591   game.exit_x = -1;
3592   game.exit_y = -1;
3593
3594   game.all_players_gone = FALSE;
3595
3596   game.LevelSolved = FALSE;
3597   game.GameOver = FALSE;
3598
3599   game.GamePlayed = !tape.playing;
3600
3601   game.LevelSolved_GameWon = FALSE;
3602   game.LevelSolved_GameEnd = FALSE;
3603   game.LevelSolved_SaveTape = FALSE;
3604   game.LevelSolved_SaveScore = FALSE;
3605
3606   game.LevelSolved_CountingTime = 0;
3607   game.LevelSolved_CountingScore = 0;
3608   game.LevelSolved_CountingHealth = 0;
3609
3610   game.panel.active = TRUE;
3611
3612   game.no_time_limit = (level.time == 0);
3613
3614   game.yamyam_content_nr = 0;
3615   game.robot_wheel_active = FALSE;
3616   game.magic_wall_active = FALSE;
3617   game.magic_wall_time_left = 0;
3618   game.light_time_left = 0;
3619   game.timegate_time_left = 0;
3620   game.switchgate_pos = 0;
3621   game.wind_direction = level.wind_direction_initial;
3622
3623   game.score = 0;
3624   game.score_final = 0;
3625
3626   game.health = MAX_HEALTH;
3627   game.health_final = MAX_HEALTH;
3628
3629   game.gems_still_needed = level.gems_needed;
3630   game.sokoban_fields_still_needed = 0;
3631   game.sokoban_objects_still_needed = 0;
3632   game.lights_still_needed = 0;
3633   game.players_still_needed = 0;
3634   game.friends_still_needed = 0;
3635
3636   game.lenses_time_left = 0;
3637   game.magnify_time_left = 0;
3638
3639   game.ball_active = level.ball_active_initial;
3640   game.ball_content_nr = 0;
3641
3642   game.explosions_delayed = TRUE;
3643
3644   game.envelope_active = FALSE;
3645
3646   for (i = 0; i < NUM_BELTS; i++)
3647   {
3648     game.belt_dir[i] = MV_NONE;
3649     game.belt_dir_nr[i] = 3;            // not moving, next moving left
3650   }
3651
3652   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3653     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3654
3655 #if DEBUG_INIT_PLAYER
3656   DebugPrintPlayerStatus("Player status at level initialization");
3657 #endif
3658
3659   SCAN_PLAYFIELD(x, y)
3660   {
3661     Feld[x][y] = Last[x][y] = level.field[x][y];
3662     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3663     ChangeDelay[x][y] = 0;
3664     ChangePage[x][y] = -1;
3665     CustomValue[x][y] = 0;              // initialized in InitField()
3666     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3667     AmoebaNr[x][y] = 0;
3668     WasJustMoving[x][y] = 0;
3669     WasJustFalling[x][y] = 0;
3670     CheckCollision[x][y] = 0;
3671     CheckImpact[x][y] = 0;
3672     Stop[x][y] = FALSE;
3673     Pushed[x][y] = FALSE;
3674
3675     ChangeCount[x][y] = 0;
3676     ChangeEvent[x][y] = -1;
3677
3678     ExplodePhase[x][y] = 0;
3679     ExplodeDelay[x][y] = 0;
3680     ExplodeField[x][y] = EX_TYPE_NONE;
3681
3682     RunnerVisit[x][y] = 0;
3683     PlayerVisit[x][y] = 0;
3684
3685     GfxFrame[x][y] = 0;
3686     GfxRandom[x][y] = INIT_GFX_RANDOM();
3687     GfxElement[x][y] = EL_UNDEFINED;
3688     GfxAction[x][y] = ACTION_DEFAULT;
3689     GfxDir[x][y] = MV_NONE;
3690     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3691   }
3692
3693   SCAN_PLAYFIELD(x, y)
3694   {
3695     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3696       emulate_bd = FALSE;
3697     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3698       emulate_sb = FALSE;
3699     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3700       emulate_sp = FALSE;
3701
3702     InitField(x, y, TRUE);
3703
3704     ResetGfxAnimation(x, y);
3705   }
3706
3707   InitBeltMovement();
3708
3709   for (i = 0; i < MAX_PLAYERS; i++)
3710   {
3711     struct PlayerInfo *player = &stored_player[i];
3712
3713     // set number of special actions for bored and sleeping animation
3714     player->num_special_action_bored =
3715       get_num_special_action(player->artwork_element,
3716                              ACTION_BORING_1, ACTION_BORING_LAST);
3717     player->num_special_action_sleeping =
3718       get_num_special_action(player->artwork_element,
3719                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3720   }
3721
3722   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3723                     emulate_sb ? EMU_SOKOBAN :
3724                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3725
3726   // initialize type of slippery elements
3727   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3728   {
3729     if (!IS_CUSTOM_ELEMENT(i))
3730     {
3731       // default: elements slip down either to the left or right randomly
3732       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3733
3734       // SP style elements prefer to slip down on the left side
3735       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3736         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3737
3738       // BD style elements prefer to slip down on the left side
3739       if (game.emulation == EMU_BOULDERDASH)
3740         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3741     }
3742   }
3743
3744   // initialize explosion and ignition delay
3745   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3746   {
3747     if (!IS_CUSTOM_ELEMENT(i))
3748     {
3749       int num_phase = 8;
3750       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3751                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3752                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3753       int last_phase = (num_phase + 1) * delay;
3754       int half_phase = (num_phase / 2) * delay;
3755
3756       element_info[i].explosion_delay = last_phase - 1;
3757       element_info[i].ignition_delay = half_phase;
3758
3759       if (i == EL_BLACK_ORB)
3760         element_info[i].ignition_delay = 1;
3761     }
3762   }
3763
3764   // correct non-moving belts to start moving left
3765   for (i = 0; i < NUM_BELTS; i++)
3766     if (game.belt_dir[i] == MV_NONE)
3767       game.belt_dir_nr[i] = 3;          // not moving, next moving left
3768
3769 #if USE_NEW_PLAYER_ASSIGNMENTS
3770   // use preferred player also in local single-player mode
3771   if (!network.enabled && !game.team_mode)
3772   {
3773     int new_index_nr = setup.network_player_nr;
3774
3775     if (new_index_nr >= 0 && new_index_nr < MAX_PLAYERS)
3776     {
3777       for (i = 0; i < MAX_PLAYERS; i++)
3778         stored_player[i].connected_locally = FALSE;
3779
3780       stored_player[new_index_nr].connected_locally = TRUE;
3781     }
3782   }
3783
3784   for (i = 0; i < MAX_PLAYERS; i++)
3785   {
3786     stored_player[i].connected = FALSE;
3787
3788     // in network game mode, the local player might not be the first player
3789     if (stored_player[i].connected_locally)
3790       local_player = &stored_player[i];
3791   }
3792
3793   if (!network.enabled)
3794     local_player->connected = TRUE;
3795
3796   if (tape.playing)
3797   {
3798     for (i = 0; i < MAX_PLAYERS; i++)
3799       stored_player[i].connected = tape.player_participates[i];
3800   }
3801   else if (network.enabled)
3802   {
3803     // add team mode players connected over the network (needed for correct
3804     // assignment of player figures from level to locally playing players)
3805
3806     for (i = 0; i < MAX_PLAYERS; i++)
3807       if (stored_player[i].connected_network)
3808         stored_player[i].connected = TRUE;
3809   }
3810   else if (game.team_mode)
3811   {
3812     // try to guess locally connected team mode players (needed for correct
3813     // assignment of player figures from level to locally playing players)
3814
3815     for (i = 0; i < MAX_PLAYERS; i++)
3816       if (setup.input[i].use_joystick ||
3817           setup.input[i].key.left != KSYM_UNDEFINED)
3818         stored_player[i].connected = TRUE;
3819   }
3820
3821 #if DEBUG_INIT_PLAYER
3822   DebugPrintPlayerStatus("Player status after level initialization");
3823 #endif
3824
3825 #if DEBUG_INIT_PLAYER
3826   if (options.debug)
3827     printf("Reassigning players ...\n");
3828 #endif
3829
3830   // check if any connected player was not found in playfield
3831   for (i = 0; i < MAX_PLAYERS; i++)
3832   {
3833     struct PlayerInfo *player = &stored_player[i];
3834
3835     if (player->connected && !player->present)
3836     {
3837       struct PlayerInfo *field_player = NULL;
3838
3839 #if DEBUG_INIT_PLAYER
3840       if (options.debug)
3841         printf("- looking for field player for player %d ...\n", i + 1);
3842 #endif
3843
3844       // assign first free player found that is present in the playfield
3845
3846       // first try: look for unmapped playfield player that is not connected
3847       for (j = 0; j < MAX_PLAYERS; j++)
3848         if (field_player == NULL &&
3849             stored_player[j].present &&
3850             !stored_player[j].mapped &&
3851             !stored_player[j].connected)
3852           field_player = &stored_player[j];
3853
3854       // second try: look for *any* unmapped playfield player
3855       for (j = 0; j < MAX_PLAYERS; j++)
3856         if (field_player == NULL &&
3857             stored_player[j].present &&
3858             !stored_player[j].mapped)
3859           field_player = &stored_player[j];
3860
3861       if (field_player != NULL)
3862       {
3863         int jx = field_player->jx, jy = field_player->jy;
3864
3865 #if DEBUG_INIT_PLAYER
3866         if (options.debug)
3867           printf("- found player %d\n", field_player->index_nr + 1);
3868 #endif
3869
3870         player->present = FALSE;
3871         player->active = FALSE;
3872
3873         field_player->present = TRUE;
3874         field_player->active = TRUE;
3875
3876         /*
3877         player->initial_element = field_player->initial_element;
3878         player->artwork_element = field_player->artwork_element;
3879
3880         player->block_last_field       = field_player->block_last_field;
3881         player->block_delay_adjustment = field_player->block_delay_adjustment;
3882         */
3883
3884         StorePlayer[jx][jy] = field_player->element_nr;
3885
3886         field_player->jx = field_player->last_jx = jx;
3887         field_player->jy = field_player->last_jy = jy;
3888
3889         if (local_player == player)
3890           local_player = field_player;
3891
3892         map_player_action[field_player->index_nr] = i;
3893
3894         field_player->mapped = TRUE;
3895
3896 #if DEBUG_INIT_PLAYER
3897         if (options.debug)
3898           printf("- map_player_action[%d] == %d\n",
3899                  field_player->index_nr + 1, i + 1);
3900 #endif
3901       }
3902     }
3903
3904     if (player->connected && player->present)
3905       player->mapped = TRUE;
3906   }
3907
3908 #if DEBUG_INIT_PLAYER
3909   DebugPrintPlayerStatus("Player status after player assignment (first stage)");
3910 #endif
3911
3912 #else
3913
3914   // check if any connected player was not found in playfield
3915   for (i = 0; i < MAX_PLAYERS; i++)
3916   {
3917     struct PlayerInfo *player = &stored_player[i];
3918
3919     if (player->connected && !player->present)
3920     {
3921       for (j = 0; j < MAX_PLAYERS; j++)
3922       {
3923         struct PlayerInfo *field_player = &stored_player[j];
3924         int jx = field_player->jx, jy = field_player->jy;
3925
3926         // assign first free player found that is present in the playfield
3927         if (field_player->present && !field_player->connected)
3928         {
3929           player->present = TRUE;
3930           player->active = TRUE;
3931
3932           field_player->present = FALSE;
3933           field_player->active = FALSE;
3934
3935           player->initial_element = field_player->initial_element;
3936           player->artwork_element = field_player->artwork_element;
3937
3938           player->block_last_field       = field_player->block_last_field;
3939           player->block_delay_adjustment = field_player->block_delay_adjustment;
3940
3941           StorePlayer[jx][jy] = player->element_nr;
3942
3943           player->jx = player->last_jx = jx;
3944           player->jy = player->last_jy = jy;
3945
3946           break;
3947         }
3948       }
3949     }
3950   }
3951 #endif
3952
3953 #if 0
3954   printf("::: local_player->present == %d\n", local_player->present);
3955 #endif
3956
3957   // set focus to local player for network games, else to all players
3958   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3959   game.centered_player_nr_next = game.centered_player_nr;
3960   game.set_centered_player = FALSE;
3961   game.set_centered_player_wrap = FALSE;
3962
3963   if (network_playing && tape.recording)
3964   {
3965     // store client dependent player focus when recording network games
3966     tape.centered_player_nr_next = game.centered_player_nr_next;
3967     tape.set_centered_player = TRUE;
3968   }
3969
3970   if (tape.playing)
3971   {
3972     // when playing a tape, eliminate all players who do not participate
3973
3974 #if USE_NEW_PLAYER_ASSIGNMENTS
3975
3976     if (!game.team_mode)
3977     {
3978       for (i = 0; i < MAX_PLAYERS; i++)
3979       {
3980         if (stored_player[i].active &&
3981             !tape.player_participates[map_player_action[i]])
3982         {
3983           struct PlayerInfo *player = &stored_player[i];
3984           int jx = player->jx, jy = player->jy;
3985
3986 #if DEBUG_INIT_PLAYER
3987           if (options.debug)
3988             printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
3989 #endif
3990
3991           player->active = FALSE;
3992           StorePlayer[jx][jy] = 0;
3993           Feld[jx][jy] = EL_EMPTY;
3994         }
3995       }
3996     }
3997
3998 #else
3999
4000     for (i = 0; i < MAX_PLAYERS; i++)
4001     {
4002       if (stored_player[i].active &&
4003           !tape.player_participates[i])
4004       {
4005         struct PlayerInfo *player = &stored_player[i];
4006         int jx = player->jx, jy = player->jy;
4007
4008         player->active = FALSE;
4009         StorePlayer[jx][jy] = 0;
4010         Feld[jx][jy] = EL_EMPTY;
4011       }
4012     }
4013 #endif
4014   }
4015   else if (!network.enabled && !game.team_mode)         // && !tape.playing
4016   {
4017     // when in single player mode, eliminate all but the local player
4018
4019     for (i = 0; i < MAX_PLAYERS; i++)
4020     {
4021       struct PlayerInfo *player = &stored_player[i];
4022
4023       if (player->active && player != local_player)
4024       {
4025         int jx = player->jx, jy = player->jy;
4026
4027         player->active = FALSE;
4028         player->present = FALSE;
4029
4030         StorePlayer[jx][jy] = 0;
4031         Feld[jx][jy] = EL_EMPTY;
4032       }
4033     }
4034   }
4035
4036   for (i = 0; i < MAX_PLAYERS; i++)
4037     if (stored_player[i].active)
4038       game.players_still_needed++;
4039
4040   if (level.solved_by_one_player)
4041     game.players_still_needed = 1;
4042
4043   // when recording the game, store which players take part in the game
4044   if (tape.recording)
4045   {
4046 #if USE_NEW_PLAYER_ASSIGNMENTS
4047     for (i = 0; i < MAX_PLAYERS; i++)
4048       if (stored_player[i].connected)
4049         tape.player_participates[i] = TRUE;
4050 #else
4051     for (i = 0; i < MAX_PLAYERS; i++)
4052       if (stored_player[i].active)
4053         tape.player_participates[i] = TRUE;
4054 #endif
4055   }
4056
4057 #if DEBUG_INIT_PLAYER
4058   DebugPrintPlayerStatus("Player status after player assignment (final stage)");
4059 #endif
4060
4061   if (BorderElement == EL_EMPTY)
4062   {
4063     SBX_Left = 0;
4064     SBX_Right = lev_fieldx - SCR_FIELDX;
4065     SBY_Upper = 0;
4066     SBY_Lower = lev_fieldy - SCR_FIELDY;
4067   }
4068   else
4069   {
4070     SBX_Left = -1;
4071     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4072     SBY_Upper = -1;
4073     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4074   }
4075
4076   if (full_lev_fieldx <= SCR_FIELDX)
4077     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4078   if (full_lev_fieldy <= SCR_FIELDY)
4079     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4080
4081   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4082     SBX_Left--;
4083   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4084     SBY_Upper--;
4085
4086   // if local player not found, look for custom element that might create
4087   // the player (make some assumptions about the right custom element)
4088   if (!local_player->present)
4089   {
4090     int start_x = 0, start_y = 0;
4091     int found_rating = 0;
4092     int found_element = EL_UNDEFINED;
4093     int player_nr = local_player->index_nr;
4094
4095     SCAN_PLAYFIELD(x, y)
4096     {
4097       int element = Feld[x][y];
4098       int content;
4099       int xx, yy;
4100       boolean is_player;
4101
4102       if (level.use_start_element[player_nr] &&
4103           level.start_element[player_nr] == element &&
4104           found_rating < 4)
4105       {
4106         start_x = x;
4107         start_y = y;
4108
4109         found_rating = 4;
4110         found_element = element;
4111       }
4112
4113       if (!IS_CUSTOM_ELEMENT(element))
4114         continue;
4115
4116       if (CAN_CHANGE(element))
4117       {
4118         for (i = 0; i < element_info[element].num_change_pages; i++)
4119         {
4120           // check for player created from custom element as single target
4121           content = element_info[element].change_page[i].target_element;
4122           is_player = ELEM_IS_PLAYER(content);
4123
4124           if (is_player && (found_rating < 3 ||
4125                             (found_rating == 3 && element < found_element)))
4126           {
4127             start_x = x;
4128             start_y = y;
4129
4130             found_rating = 3;
4131             found_element = element;
4132           }
4133         }
4134       }
4135
4136       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4137       {
4138         // check for player created from custom element as explosion content
4139         content = element_info[element].content.e[xx][yy];
4140         is_player = ELEM_IS_PLAYER(content);
4141
4142         if (is_player && (found_rating < 2 ||
4143                           (found_rating == 2 && element < found_element)))
4144         {
4145           start_x = x + xx - 1;
4146           start_y = y + yy - 1;
4147
4148           found_rating = 2;
4149           found_element = element;
4150         }
4151
4152         if (!CAN_CHANGE(element))
4153           continue;
4154
4155         for (i = 0; i < element_info[element].num_change_pages; i++)
4156         {
4157           // check for player created from custom element as extended target
4158           content =
4159             element_info[element].change_page[i].target_content.e[xx][yy];
4160
4161           is_player = ELEM_IS_PLAYER(content);
4162
4163           if (is_player && (found_rating < 1 ||
4164                             (found_rating == 1 && element < found_element)))
4165           {
4166             start_x = x + xx - 1;
4167             start_y = y + yy - 1;
4168
4169             found_rating = 1;
4170             found_element = element;
4171           }
4172         }
4173       }
4174     }
4175
4176     scroll_x = SCROLL_POSITION_X(start_x);
4177     scroll_y = SCROLL_POSITION_Y(start_y);
4178   }
4179   else
4180   {
4181     scroll_x = SCROLL_POSITION_X(local_player->jx);
4182     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4183   }
4184
4185   // !!! FIX THIS (START) !!!
4186   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4187   {
4188     InitGameEngine_EM();
4189   }
4190   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4191   {
4192     InitGameEngine_SP();
4193   }
4194   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4195   {
4196     InitGameEngine_MM();
4197   }
4198   else
4199   {
4200     DrawLevel(REDRAW_FIELD);
4201     DrawAllPlayers();
4202
4203     // after drawing the level, correct some elements
4204     if (game.timegate_time_left == 0)
4205       CloseAllOpenTimegates();
4206   }
4207
4208   // blit playfield from scroll buffer to normal back buffer for fading in
4209   BlitScreenToBitmap(backbuffer);
4210   // !!! FIX THIS (END) !!!
4211
4212   DrawMaskedBorder(fade_mask);
4213
4214   FadeIn(fade_mask);
4215
4216 #if 1
4217   // full screen redraw is required at this point in the following cases:
4218   // - special editor door undrawn when game was started from level editor
4219   // - drawing area (playfield) was changed and has to be removed completely
4220   redraw_mask = REDRAW_ALL;
4221   BackToFront();
4222 #endif
4223
4224   if (!game.restart_level)
4225   {
4226     // copy default game door content to main double buffer
4227
4228     // !!! CHECK AGAIN !!!
4229     SetPanelBackground();
4230     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4231     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4232   }
4233
4234   SetPanelBackground();
4235   SetDrawBackgroundMask(REDRAW_DOOR_1);
4236
4237   UpdateAndDisplayGameControlValues();
4238
4239   if (!game.restart_level)
4240   {
4241     UnmapGameButtons();
4242     UnmapTapeButtons();
4243
4244     FreeGameButtons();
4245     CreateGameButtons();
4246
4247     MapGameButtons();
4248     MapTapeButtons();
4249
4250     // copy actual game door content to door double buffer for OpenDoor()
4251     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4252
4253     OpenDoor(DOOR_OPEN_ALL);
4254
4255     KeyboardAutoRepeatOffUnlessAutoplay();
4256
4257 #if DEBUG_INIT_PLAYER
4258     DebugPrintPlayerStatus("Player status (final)");
4259 #endif
4260   }
4261
4262   UnmapAllGadgets();
4263
4264   MapGameButtons();
4265   MapTapeButtons();
4266
4267   if (!game.restart_level && !tape.playing)
4268   {
4269     LevelStats_incPlayed(level_nr);
4270
4271     SaveLevelSetup_SeriesInfo();
4272   }
4273
4274   game.restart_level = FALSE;
4275   game.restart_game_message = NULL;
4276   game.request_active = FALSE;
4277
4278   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4279     InitGameActions_MM();
4280
4281   SaveEngineSnapshotToListInitial();
4282
4283   if (!game.restart_level)
4284   {
4285     PlaySound(SND_GAME_STARTING);
4286
4287     if (setup.sound_music)
4288       PlayLevelMusic();
4289   }
4290 }
4291
4292 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4293                         int actual_player_x, int actual_player_y)
4294 {
4295   // this is used for non-R'n'D game engines to update certain engine values
4296
4297   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4298   {
4299     actual_player_x = correctLevelPosX_EM(actual_player_x);
4300     actual_player_y = correctLevelPosY_EM(actual_player_y);
4301   }
4302
4303   // needed to determine if sounds are played within the visible screen area
4304   scroll_x = actual_scroll_x;
4305   scroll_y = actual_scroll_y;
4306
4307   // needed to get player position for "follow finger" playing input method
4308   local_player->jx = actual_player_x;
4309   local_player->jy = actual_player_y;
4310 }
4311
4312 void InitMovDir(int x, int y)
4313 {
4314   int i, element = Feld[x][y];
4315   static int xy[4][2] =
4316   {
4317     {  0, +1 },
4318     { +1,  0 },
4319     {  0, -1 },
4320     { -1,  0 }
4321   };
4322   static int direction[3][4] =
4323   {
4324     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4325     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4326     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4327   };
4328
4329   switch (element)
4330   {
4331     case EL_BUG_RIGHT:
4332     case EL_BUG_UP:
4333     case EL_BUG_LEFT:
4334     case EL_BUG_DOWN:
4335       Feld[x][y] = EL_BUG;
4336       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4337       break;
4338
4339     case EL_SPACESHIP_RIGHT:
4340     case EL_SPACESHIP_UP:
4341     case EL_SPACESHIP_LEFT:
4342     case EL_SPACESHIP_DOWN:
4343       Feld[x][y] = EL_SPACESHIP;
4344       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4345       break;
4346
4347     case EL_BD_BUTTERFLY_RIGHT:
4348     case EL_BD_BUTTERFLY_UP:
4349     case EL_BD_BUTTERFLY_LEFT:
4350     case EL_BD_BUTTERFLY_DOWN:
4351       Feld[x][y] = EL_BD_BUTTERFLY;
4352       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4353       break;
4354
4355     case EL_BD_FIREFLY_RIGHT:
4356     case EL_BD_FIREFLY_UP:
4357     case EL_BD_FIREFLY_LEFT:
4358     case EL_BD_FIREFLY_DOWN:
4359       Feld[x][y] = EL_BD_FIREFLY;
4360       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4361       break;
4362
4363     case EL_PACMAN_RIGHT:
4364     case EL_PACMAN_UP:
4365     case EL_PACMAN_LEFT:
4366     case EL_PACMAN_DOWN:
4367       Feld[x][y] = EL_PACMAN;
4368       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4369       break;
4370
4371     case EL_YAMYAM_LEFT:
4372     case EL_YAMYAM_RIGHT:
4373     case EL_YAMYAM_UP:
4374     case EL_YAMYAM_DOWN:
4375       Feld[x][y] = EL_YAMYAM;
4376       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4377       break;
4378
4379     case EL_SP_SNIKSNAK:
4380       MovDir[x][y] = MV_UP;
4381       break;
4382
4383     case EL_SP_ELECTRON:
4384       MovDir[x][y] = MV_LEFT;
4385       break;
4386
4387     case EL_MOLE_LEFT:
4388     case EL_MOLE_RIGHT:
4389     case EL_MOLE_UP:
4390     case EL_MOLE_DOWN:
4391       Feld[x][y] = EL_MOLE;
4392       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4393       break;
4394
4395     default:
4396       if (IS_CUSTOM_ELEMENT(element))
4397       {
4398         struct ElementInfo *ei = &element_info[element];
4399         int move_direction_initial = ei->move_direction_initial;
4400         int move_pattern = ei->move_pattern;
4401
4402         if (move_direction_initial == MV_START_PREVIOUS)
4403         {
4404           if (MovDir[x][y] != MV_NONE)
4405             return;
4406
4407           move_direction_initial = MV_START_AUTOMATIC;
4408         }
4409
4410         if (move_direction_initial == MV_START_RANDOM)
4411           MovDir[x][y] = 1 << RND(4);
4412         else if (move_direction_initial & MV_ANY_DIRECTION)
4413           MovDir[x][y] = move_direction_initial;
4414         else if (move_pattern == MV_ALL_DIRECTIONS ||
4415                  move_pattern == MV_TURNING_LEFT ||
4416                  move_pattern == MV_TURNING_RIGHT ||
4417                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4418                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4419                  move_pattern == MV_TURNING_RANDOM)
4420           MovDir[x][y] = 1 << RND(4);
4421         else if (move_pattern == MV_HORIZONTAL)
4422           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4423         else if (move_pattern == MV_VERTICAL)
4424           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4425         else if (move_pattern & MV_ANY_DIRECTION)
4426           MovDir[x][y] = element_info[element].move_pattern;
4427         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4428                  move_pattern == MV_ALONG_RIGHT_SIDE)
4429         {
4430           // use random direction as default start direction
4431           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4432             MovDir[x][y] = 1 << RND(4);
4433
4434           for (i = 0; i < NUM_DIRECTIONS; i++)
4435           {
4436             int x1 = x + xy[i][0];
4437             int y1 = y + xy[i][1];
4438
4439             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4440             {
4441               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4442                 MovDir[x][y] = direction[0][i];
4443               else
4444                 MovDir[x][y] = direction[1][i];
4445
4446               break;
4447             }
4448           }
4449         }                
4450       }
4451       else
4452       {
4453         MovDir[x][y] = 1 << RND(4);
4454
4455         if (element != EL_BUG &&
4456             element != EL_SPACESHIP &&
4457             element != EL_BD_BUTTERFLY &&
4458             element != EL_BD_FIREFLY)
4459           break;
4460
4461         for (i = 0; i < NUM_DIRECTIONS; i++)
4462         {
4463           int x1 = x + xy[i][0];
4464           int y1 = y + xy[i][1];
4465
4466           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4467           {
4468             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4469             {
4470               MovDir[x][y] = direction[0][i];
4471               break;
4472             }
4473             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4474                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4475             {
4476               MovDir[x][y] = direction[1][i];
4477               break;
4478             }
4479           }
4480         }
4481       }
4482       break;
4483   }
4484
4485   GfxDir[x][y] = MovDir[x][y];
4486 }
4487
4488 void InitAmoebaNr(int x, int y)
4489 {
4490   int i;
4491   int group_nr = AmoebeNachbarNr(x, y);
4492
4493   if (group_nr == 0)
4494   {
4495     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4496     {
4497       if (AmoebaCnt[i] == 0)
4498       {
4499         group_nr = i;
4500         break;
4501       }
4502     }
4503   }
4504
4505   AmoebaNr[x][y] = group_nr;
4506   AmoebaCnt[group_nr]++;
4507   AmoebaCnt2[group_nr]++;
4508 }
4509
4510 static void LevelSolved(void)
4511 {
4512   if (level.game_engine_type == GAME_ENGINE_TYPE_RND &&
4513       game.players_still_needed > 0)
4514     return;
4515
4516   game.LevelSolved = TRUE;
4517   game.GameOver = TRUE;
4518
4519   game.score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4520                       game_em.lev->score :
4521                       level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4522                       game_mm.score :
4523                       game.score);
4524   game.health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4525                        MM_HEALTH(game_mm.laser_overload_value) :
4526                        game.health);
4527
4528   game.LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed : TimeLeft);
4529   game.LevelSolved_CountingScore = game.score_final;
4530   game.LevelSolved_CountingHealth = game.health_final;
4531 }
4532
4533 void GameWon(void)
4534 {
4535   static int time_count_steps;
4536   static int time, time_final;
4537   static int score, score_final;
4538   static int health, health_final;
4539   static int game_over_delay_1 = 0;
4540   static int game_over_delay_2 = 0;
4541   static int game_over_delay_3 = 0;
4542   int game_over_delay_value_1 = 50;
4543   int game_over_delay_value_2 = 25;
4544   int game_over_delay_value_3 = 50;
4545
4546   if (!game.LevelSolved_GameWon)
4547   {
4548     int i;
4549
4550     // do not start end game actions before the player stops moving (to exit)
4551     if (local_player->active && local_player->MovPos)
4552       return;
4553
4554     game.LevelSolved_GameWon = TRUE;
4555     game.LevelSolved_SaveTape = tape.recording;
4556     game.LevelSolved_SaveScore = !tape.playing;
4557
4558     if (!tape.playing)
4559     {
4560       LevelStats_incSolved(level_nr);
4561
4562       SaveLevelSetup_SeriesInfo();
4563     }
4564
4565     if (tape.auto_play)         // tape might already be stopped here
4566       tape.auto_play_level_solved = TRUE;
4567
4568     TapeStop();
4569
4570     game_over_delay_1 = 0;
4571     game_over_delay_2 = 0;
4572     game_over_delay_3 = game_over_delay_value_3;
4573
4574     time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4575     score = score_final = game.score_final;
4576     health = health_final = game.health_final;
4577
4578     if (level.score[SC_TIME_BONUS] > 0)
4579     {
4580       if (TimeLeft > 0)
4581       {
4582         time_final = 0;
4583         score_final += TimeLeft * level.score[SC_TIME_BONUS];
4584       }
4585       else if (game.no_time_limit && TimePlayed < 999)
4586       {
4587         time_final = 999;
4588         score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4589       }
4590
4591       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4592
4593       game_over_delay_1 = game_over_delay_value_1;
4594
4595       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4596       {
4597         health_final = 0;
4598         score_final += health * level.score[SC_TIME_BONUS];
4599
4600         game_over_delay_2 = game_over_delay_value_2;
4601       }
4602
4603       game.score_final = score_final;
4604       game.health_final = health_final;
4605     }
4606
4607     if (level_editor_test_game)
4608     {
4609       time = time_final;
4610       score = score_final;
4611
4612       game.LevelSolved_CountingTime = time;
4613       game.LevelSolved_CountingScore = score;
4614
4615       game_panel_controls[GAME_PANEL_TIME].value = time;
4616       game_panel_controls[GAME_PANEL_SCORE].value = score;
4617
4618       DisplayGameControlValues();
4619     }
4620
4621     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4622     {
4623       // check if last player has left the level
4624       if (game.exit_x >= 0 &&
4625           game.exit_y >= 0)
4626       {
4627         int x = game.exit_x;
4628         int y = game.exit_y;
4629         int element = Feld[x][y];
4630
4631         // close exit door after last player
4632         if ((game.all_players_gone &&
4633              (element == EL_EXIT_OPEN ||
4634               element == EL_SP_EXIT_OPEN ||
4635               element == EL_STEEL_EXIT_OPEN)) ||
4636             element == EL_EM_EXIT_OPEN ||
4637             element == EL_EM_STEEL_EXIT_OPEN)
4638         {
4639
4640           Feld[x][y] =
4641             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4642              element == EL_EM_EXIT_OPEN         ? EL_EM_EXIT_CLOSING :
4643              element == EL_SP_EXIT_OPEN         ? EL_SP_EXIT_CLOSING:
4644              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4645              EL_EM_STEEL_EXIT_CLOSING);
4646
4647           PlayLevelSoundElementAction(x, y, element, ACTION_CLOSING);
4648         }
4649
4650         // player disappears
4651         DrawLevelField(x, y);
4652       }
4653
4654       for (i = 0; i < MAX_PLAYERS; i++)
4655       {
4656         struct PlayerInfo *player = &stored_player[i];
4657
4658         if (player->present)
4659         {
4660           RemovePlayer(player);
4661
4662           // player disappears
4663           DrawLevelField(player->jx, player->jy);
4664         }
4665       }
4666     }
4667
4668     PlaySound(SND_GAME_WINNING);
4669   }
4670
4671   if (game_over_delay_1 > 0)
4672   {
4673     game_over_delay_1--;
4674
4675     return;
4676   }
4677
4678   if (time != time_final)
4679   {
4680     int time_to_go = ABS(time_final - time);
4681     int time_count_dir = (time < time_final ? +1 : -1);
4682
4683     if (time_to_go < time_count_steps)
4684       time_count_steps = 1;
4685
4686     time  += time_count_steps * time_count_dir;
4687     score += time_count_steps * level.score[SC_TIME_BONUS];
4688
4689     game.LevelSolved_CountingTime = time;
4690     game.LevelSolved_CountingScore = score;
4691
4692     game_panel_controls[GAME_PANEL_TIME].value = time;
4693     game_panel_controls[GAME_PANEL_SCORE].value = score;
4694
4695     DisplayGameControlValues();
4696
4697     if (time == time_final)
4698       StopSound(SND_GAME_LEVELTIME_BONUS);
4699     else if (setup.sound_loops)
4700       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4701     else
4702       PlaySound(SND_GAME_LEVELTIME_BONUS);
4703
4704     return;
4705   }
4706
4707   if (game_over_delay_2 > 0)
4708   {
4709     game_over_delay_2--;
4710
4711     return;
4712   }
4713
4714   if (health != health_final)
4715   {
4716     int health_count_dir = (health < health_final ? +1 : -1);
4717
4718     health += health_count_dir;
4719     score  += level.score[SC_TIME_BONUS];
4720
4721     game.LevelSolved_CountingHealth = health;
4722     game.LevelSolved_CountingScore = score;
4723
4724     game_panel_controls[GAME_PANEL_HEALTH].value = health;
4725     game_panel_controls[GAME_PANEL_SCORE].value = score;
4726
4727     DisplayGameControlValues();
4728
4729     if (health == health_final)
4730       StopSound(SND_GAME_LEVELTIME_BONUS);
4731     else if (setup.sound_loops)
4732       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4733     else
4734       PlaySound(SND_GAME_LEVELTIME_BONUS);
4735
4736     return;
4737   }
4738
4739   game.panel.active = FALSE;
4740
4741   if (game_over_delay_3 > 0)
4742   {
4743     game_over_delay_3--;
4744
4745     return;
4746   }
4747
4748   GameEnd();
4749 }
4750
4751 void GameEnd(void)
4752 {
4753   // used instead of "level_nr" (needed for network games)
4754   int last_level_nr = levelset.level_nr;
4755   int hi_pos;
4756
4757   game.LevelSolved_GameEnd = TRUE;
4758
4759   if (game.LevelSolved_SaveTape)
4760   {
4761     // make sure that request dialog to save tape does not open door again
4762     if (!global.use_envelope_request)
4763       CloseDoor(DOOR_CLOSE_1);
4764
4765     SaveTapeChecked_LevelSolved(tape.level_nr);         // ask to save tape
4766   }
4767
4768   // if no tape is to be saved, close both doors simultaneously
4769   CloseDoor(DOOR_CLOSE_ALL);
4770
4771   if (level_editor_test_game)
4772   {
4773     SetGameStatus(GAME_MODE_MAIN);
4774
4775     DrawMainMenu();
4776
4777     return;
4778   }
4779
4780   if (!game.LevelSolved_SaveScore)
4781   {
4782     SetGameStatus(GAME_MODE_MAIN);
4783
4784     DrawMainMenu();
4785
4786     return;
4787   }
4788
4789   if (level_nr == leveldir_current->handicap_level)
4790   {
4791     leveldir_current->handicap_level++;
4792
4793     SaveLevelSetup_SeriesInfo();
4794   }
4795
4796   if (setup.increment_levels &&
4797       level_nr < leveldir_current->last_level &&
4798       !network_playing)
4799   {
4800     level_nr++;         // advance to next level
4801     TapeErase();        // start with empty tape
4802
4803     if (setup.auto_play_next_level)
4804     {
4805       LoadLevel(level_nr);
4806
4807       SaveLevelSetup_SeriesInfo();
4808     }
4809   }
4810
4811   hi_pos = NewHiScore(last_level_nr);
4812
4813   if (hi_pos >= 0 && !setup.skip_scores_after_game)
4814   {
4815     SetGameStatus(GAME_MODE_SCORES);
4816
4817     DrawHallOfFame(last_level_nr, hi_pos);
4818   }
4819   else if (setup.auto_play_next_level && setup.increment_levels &&
4820            last_level_nr < leveldir_current->last_level &&
4821            !network_playing)
4822   {
4823     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
4824   }
4825   else
4826   {
4827     SetGameStatus(GAME_MODE_MAIN);
4828
4829     DrawMainMenu();
4830   }
4831 }
4832
4833 int NewHiScore(int level_nr)
4834 {
4835   int k, l;
4836   int position = -1;
4837   boolean one_score_entry_per_name = !program.many_scores_per_name;
4838
4839   LoadScore(level_nr);
4840
4841   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4842       game.score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
4843     return -1;
4844
4845   for (k = 0; k < MAX_SCORE_ENTRIES; k++)
4846   {
4847     if (game.score_final > highscore[k].Score)
4848     {
4849       // player has made it to the hall of fame
4850
4851       if (k < MAX_SCORE_ENTRIES - 1)
4852       {
4853         int m = MAX_SCORE_ENTRIES - 1;
4854
4855         if (one_score_entry_per_name)
4856         {
4857           for (l = k; l < MAX_SCORE_ENTRIES; l++)
4858             if (strEqual(setup.player_name, highscore[l].Name))
4859               m = l;
4860
4861           if (m == k)   // player's new highscore overwrites his old one
4862             goto put_into_list;
4863         }
4864
4865         for (l = m; l > k; l--)
4866         {
4867           strcpy(highscore[l].Name, highscore[l - 1].Name);
4868           highscore[l].Score = highscore[l - 1].Score;
4869         }
4870       }
4871
4872       put_into_list:
4873
4874       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4875       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4876       highscore[k].Score = game.score_final;
4877       position = k;
4878
4879       break;
4880     }
4881     else if (one_score_entry_per_name &&
4882              !strncmp(setup.player_name, highscore[k].Name,
4883                       MAX_PLAYER_NAME_LEN))
4884       break;    // player already there with a higher score
4885   }
4886
4887   if (position >= 0) 
4888     SaveScore(level_nr);
4889
4890   return position;
4891 }
4892
4893 static int getElementMoveStepsizeExt(int x, int y, int direction)
4894 {
4895   int element = Feld[x][y];
4896   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4897   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4898   int horiz_move = (dx != 0);
4899   int sign = (horiz_move ? dx : dy);
4900   int step = sign * element_info[element].move_stepsize;
4901
4902   // special values for move stepsize for spring and things on conveyor belt
4903   if (horiz_move)
4904   {
4905     if (CAN_FALL(element) &&
4906         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4907       step = sign * MOVE_STEPSIZE_NORMAL / 2;
4908     else if (element == EL_SPRING)
4909       step = sign * MOVE_STEPSIZE_NORMAL * 2;
4910   }
4911
4912   return step;
4913 }
4914
4915 static int getElementMoveStepsize(int x, int y)
4916 {
4917   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4918 }
4919
4920 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4921 {
4922   if (player->GfxAction != action || player->GfxDir != dir)
4923   {
4924     player->GfxAction = action;
4925     player->GfxDir = dir;
4926     player->Frame = 0;
4927     player->StepFrame = 0;
4928   }
4929 }
4930
4931 static void ResetGfxFrame(int x, int y)
4932 {
4933   // profiling showed that "autotest" spends 10~20% of its time in this function
4934   if (DrawingDeactivatedField())
4935     return;
4936
4937   int element = Feld[x][y];
4938   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4939
4940   if (graphic_info[graphic].anim_global_sync)
4941     GfxFrame[x][y] = FrameCounter;
4942   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4943     GfxFrame[x][y] = CustomValue[x][y];
4944   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4945     GfxFrame[x][y] = element_info[element].collect_score;
4946   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4947     GfxFrame[x][y] = ChangeDelay[x][y];
4948 }
4949
4950 static void ResetGfxAnimation(int x, int y)
4951 {
4952   GfxAction[x][y] = ACTION_DEFAULT;
4953   GfxDir[x][y] = MovDir[x][y];
4954   GfxFrame[x][y] = 0;
4955
4956   ResetGfxFrame(x, y);
4957 }
4958
4959 static void ResetRandomAnimationValue(int x, int y)
4960 {
4961   GfxRandom[x][y] = INIT_GFX_RANDOM();
4962 }
4963
4964 static void InitMovingField(int x, int y, int direction)
4965 {
4966   int element = Feld[x][y];
4967   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4968   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4969   int newx = x + dx;
4970   int newy = y + dy;
4971   boolean is_moving_before, is_moving_after;
4972
4973   // check if element was/is moving or being moved before/after mode change
4974   is_moving_before = (WasJustMoving[x][y] != 0);
4975   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
4976
4977   // reset animation only for moving elements which change direction of moving
4978   // or which just started or stopped moving
4979   // (else CEs with property "can move" / "not moving" are reset each frame)
4980   if (is_moving_before != is_moving_after ||
4981       direction != MovDir[x][y])
4982     ResetGfxAnimation(x, y);
4983
4984   MovDir[x][y] = direction;
4985   GfxDir[x][y] = direction;
4986
4987   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4988                      direction == MV_DOWN && CAN_FALL(element) ?
4989                      ACTION_FALLING : ACTION_MOVING);
4990
4991   // this is needed for CEs with property "can move" / "not moving"
4992
4993   if (is_moving_after)
4994   {
4995     if (Feld[newx][newy] == EL_EMPTY)
4996       Feld[newx][newy] = EL_BLOCKED;
4997
4998     MovDir[newx][newy] = MovDir[x][y];
4999
5000     CustomValue[newx][newy] = CustomValue[x][y];
5001
5002     GfxFrame[newx][newy] = GfxFrame[x][y];
5003     GfxRandom[newx][newy] = GfxRandom[x][y];
5004     GfxAction[newx][newy] = GfxAction[x][y];
5005     GfxDir[newx][newy] = GfxDir[x][y];
5006   }
5007 }
5008
5009 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5010 {
5011   int direction = MovDir[x][y];
5012   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5013   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5014
5015   *goes_to_x = newx;
5016   *goes_to_y = newy;
5017 }
5018
5019 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5020 {
5021   int oldx = x, oldy = y;
5022   int direction = MovDir[x][y];
5023
5024   if (direction == MV_LEFT)
5025     oldx++;
5026   else if (direction == MV_RIGHT)
5027     oldx--;
5028   else if (direction == MV_UP)
5029     oldy++;
5030   else if (direction == MV_DOWN)
5031     oldy--;
5032
5033   *comes_from_x = oldx;
5034   *comes_from_y = oldy;
5035 }
5036
5037 static int MovingOrBlocked2Element(int x, int y)
5038 {
5039   int element = Feld[x][y];
5040
5041   if (element == EL_BLOCKED)
5042   {
5043     int oldx, oldy;
5044
5045     Blocked2Moving(x, y, &oldx, &oldy);
5046     return Feld[oldx][oldy];
5047   }
5048   else
5049     return element;
5050 }
5051
5052 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5053 {
5054   // like MovingOrBlocked2Element(), but if element is moving
5055   // and (x,y) is the field the moving element is just leaving,
5056   // return EL_BLOCKED instead of the element value
5057   int element = Feld[x][y];
5058
5059   if (IS_MOVING(x, y))
5060   {
5061     if (element == EL_BLOCKED)
5062     {
5063       int oldx, oldy;
5064
5065       Blocked2Moving(x, y, &oldx, &oldy);
5066       return Feld[oldx][oldy];
5067     }
5068     else
5069       return EL_BLOCKED;
5070   }
5071   else
5072     return element;
5073 }
5074
5075 static void RemoveField(int x, int y)
5076 {
5077   Feld[x][y] = EL_EMPTY;
5078
5079   MovPos[x][y] = 0;
5080   MovDir[x][y] = 0;
5081   MovDelay[x][y] = 0;
5082
5083   CustomValue[x][y] = 0;
5084
5085   AmoebaNr[x][y] = 0;
5086   ChangeDelay[x][y] = 0;
5087   ChangePage[x][y] = -1;
5088   Pushed[x][y] = FALSE;
5089
5090   GfxElement[x][y] = EL_UNDEFINED;
5091   GfxAction[x][y] = ACTION_DEFAULT;
5092   GfxDir[x][y] = MV_NONE;
5093 }
5094
5095 static void RemoveMovingField(int x, int y)
5096 {
5097   int oldx = x, oldy = y, newx = x, newy = y;
5098   int element = Feld[x][y];
5099   int next_element = EL_UNDEFINED;
5100
5101   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5102     return;
5103
5104   if (IS_MOVING(x, y))
5105   {
5106     Moving2Blocked(x, y, &newx, &newy);
5107
5108     if (Feld[newx][newy] != EL_BLOCKED)
5109     {
5110       // element is moving, but target field is not free (blocked), but
5111       // already occupied by something different (example: acid pool);
5112       // in this case, only remove the moving field, but not the target
5113
5114       RemoveField(oldx, oldy);
5115
5116       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5117
5118       TEST_DrawLevelField(oldx, oldy);
5119
5120       return;
5121     }
5122   }
5123   else if (element == EL_BLOCKED)
5124   {
5125     Blocked2Moving(x, y, &oldx, &oldy);
5126     if (!IS_MOVING(oldx, oldy))
5127       return;
5128   }
5129
5130   if (element == EL_BLOCKED &&
5131       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5132        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5133        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5134        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5135        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5136        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5137     next_element = get_next_element(Feld[oldx][oldy]);
5138
5139   RemoveField(oldx, oldy);
5140   RemoveField(newx, newy);
5141
5142   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5143
5144   if (next_element != EL_UNDEFINED)
5145     Feld[oldx][oldy] = next_element;
5146
5147   TEST_DrawLevelField(oldx, oldy);
5148   TEST_DrawLevelField(newx, newy);
5149 }
5150
5151 void DrawDynamite(int x, int y)
5152 {
5153   int sx = SCREENX(x), sy = SCREENY(y);
5154   int graphic = el2img(Feld[x][y]);
5155   int frame;
5156
5157   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5158     return;
5159
5160   if (IS_WALKABLE_INSIDE(Back[x][y]))
5161     return;
5162
5163   if (Back[x][y])
5164     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5165   else if (Store[x][y])
5166     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5167
5168   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5169
5170   if (Back[x][y] || Store[x][y])
5171     DrawGraphicThruMask(sx, sy, graphic, frame);
5172   else
5173     DrawGraphic(sx, sy, graphic, frame);
5174 }
5175
5176 static void CheckDynamite(int x, int y)
5177 {
5178   if (MovDelay[x][y] != 0)      // dynamite is still waiting to explode
5179   {
5180     MovDelay[x][y]--;
5181
5182     if (MovDelay[x][y] != 0)
5183     {
5184       DrawDynamite(x, y);
5185       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5186
5187       return;
5188     }
5189   }
5190
5191   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5192
5193   Bang(x, y);
5194 }
5195
5196 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5197 {
5198   boolean num_checked_players = 0;
5199   int i;
5200
5201   for (i = 0; i < MAX_PLAYERS; i++)
5202   {
5203     if (stored_player[i].active)
5204     {
5205       int sx = stored_player[i].jx;
5206       int sy = stored_player[i].jy;
5207
5208       if (num_checked_players == 0)
5209       {
5210         *sx1 = *sx2 = sx;
5211         *sy1 = *sy2 = sy;
5212       }
5213       else
5214       {
5215         *sx1 = MIN(*sx1, sx);
5216         *sy1 = MIN(*sy1, sy);
5217         *sx2 = MAX(*sx2, sx);
5218         *sy2 = MAX(*sy2, sy);
5219       }
5220
5221       num_checked_players++;
5222     }
5223   }
5224 }
5225
5226 static boolean checkIfAllPlayersFitToScreen_RND(void)
5227 {
5228   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5229
5230   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5231
5232   return (sx2 - sx1 < SCR_FIELDX &&
5233           sy2 - sy1 < SCR_FIELDY);
5234 }
5235
5236 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5237 {
5238   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5239
5240   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5241
5242   *sx = (sx1 + sx2) / 2;
5243   *sy = (sy1 + sy2) / 2;
5244 }
5245
5246 static void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5247                                boolean center_screen, boolean quick_relocation)
5248 {
5249   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5250   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5251   boolean no_delay = (tape.warp_forward);
5252   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5253   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5254   int new_scroll_x, new_scroll_y;
5255
5256   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5257   {
5258     // case 1: quick relocation inside visible screen (without scrolling)
5259
5260     RedrawPlayfield();
5261
5262     return;
5263   }
5264
5265   if (!level.shifted_relocation || center_screen)
5266   {
5267     // relocation _with_ centering of screen
5268
5269     new_scroll_x = SCROLL_POSITION_X(x);
5270     new_scroll_y = SCROLL_POSITION_Y(y);
5271   }
5272   else
5273   {
5274     // relocation _without_ centering of screen
5275
5276     int center_scroll_x = SCROLL_POSITION_X(old_x);
5277     int center_scroll_y = SCROLL_POSITION_Y(old_y);
5278     int offset_x = x + (scroll_x - center_scroll_x);
5279     int offset_y = y + (scroll_y - center_scroll_y);
5280
5281     // for new screen position, apply previous offset to center position
5282     new_scroll_x = SCROLL_POSITION_X(offset_x);
5283     new_scroll_y = SCROLL_POSITION_Y(offset_y);
5284   }
5285
5286   if (quick_relocation)
5287   {
5288     // case 2: quick relocation (redraw without visible scrolling)
5289
5290     scroll_x = new_scroll_x;
5291     scroll_y = new_scroll_y;
5292
5293     RedrawPlayfield();
5294
5295     return;
5296   }
5297
5298   // case 3: visible relocation (with scrolling to new position)
5299
5300   ScrollScreen(NULL, SCROLL_GO_ON);     // scroll last frame to full tile
5301
5302   SetVideoFrameDelay(wait_delay_value);
5303
5304   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5305   {
5306     int dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5307     int dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5308
5309     if (dx == 0 && dy == 0)             // no scrolling needed at all
5310       break;
5311
5312     scroll_x -= dx;
5313     scroll_y -= dy;
5314
5315     // set values for horizontal/vertical screen scrolling (half tile size)
5316     int dir_x = (dx != 0 ? MV_HORIZONTAL : 0);
5317     int dir_y = (dy != 0 ? MV_VERTICAL   : 0);
5318     int pos_x = dx * TILEX / 2;
5319     int pos_y = dy * TILEY / 2;
5320     int fx = getFieldbufferOffsetX_RND(dir_x, pos_x);
5321     int fy = getFieldbufferOffsetY_RND(dir_y, pos_y);
5322
5323     ScrollLevel(dx, dy);
5324     DrawAllPlayers();
5325
5326     // scroll in two steps of half tile size to make things smoother
5327     BlitScreenToBitmapExt_RND(window, fx, fy);
5328
5329     // scroll second step to align at full tile size
5330     BlitScreenToBitmap(window);
5331   }
5332
5333   DrawAllPlayers();
5334   BackToFront();
5335
5336   SetVideoFrameDelay(frame_delay_value_old);
5337 }
5338
5339 static void RelocatePlayer(int jx, int jy, int el_player_raw)
5340 {
5341   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5342   int player_nr = GET_PLAYER_NR(el_player);
5343   struct PlayerInfo *player = &stored_player[player_nr];
5344   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5345   boolean no_delay = (tape.warp_forward);
5346   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5347   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5348   int old_jx = player->jx;
5349   int old_jy = player->jy;
5350   int old_element = Feld[old_jx][old_jy];
5351   int element = Feld[jx][jy];
5352   boolean player_relocated = (old_jx != jx || old_jy != jy);
5353
5354   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5355   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5356   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5357   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5358   int leave_side_horiz = move_dir_horiz;
5359   int leave_side_vert  = move_dir_vert;
5360   int enter_side = enter_side_horiz | enter_side_vert;
5361   int leave_side = leave_side_horiz | leave_side_vert;
5362
5363   if (player->buried)           // do not reanimate dead player
5364     return;
5365
5366   if (!player_relocated)        // no need to relocate the player
5367     return;
5368
5369   if (IS_PLAYER(jx, jy))        // player already placed at new position
5370   {
5371     RemoveField(jx, jy);        // temporarily remove newly placed player
5372     DrawLevelField(jx, jy);
5373   }
5374
5375   if (player->present)
5376   {
5377     while (player->MovPos)
5378     {
5379       ScrollPlayer(player, SCROLL_GO_ON);
5380       ScrollScreen(NULL, SCROLL_GO_ON);
5381
5382       AdvanceFrameAndPlayerCounters(player->index_nr);
5383
5384       DrawPlayer(player);
5385
5386       BackToFront_WithFrameDelay(wait_delay_value);
5387     }
5388
5389     DrawPlayer(player);         // needed here only to cleanup last field
5390     DrawLevelField(player->jx, player->jy);     // remove player graphic
5391
5392     player->is_moving = FALSE;
5393   }
5394
5395   if (IS_CUSTOM_ELEMENT(old_element))
5396     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5397                                CE_LEFT_BY_PLAYER,
5398                                player->index_bit, leave_side);
5399
5400   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5401                                       CE_PLAYER_LEAVES_X,
5402                                       player->index_bit, leave_side);
5403
5404   Feld[jx][jy] = el_player;
5405   InitPlayerField(jx, jy, el_player, TRUE);
5406
5407   /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5408      possible that the relocation target field did not contain a player element,
5409      but a walkable element, to which the new player was relocated -- in this
5410      case, restore that (already initialized!) element on the player field */
5411   if (!ELEM_IS_PLAYER(element)) // player may be set on walkable element
5412   {
5413     Feld[jx][jy] = element;     // restore previously existing element
5414   }
5415
5416   // only visually relocate centered player
5417   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5418                      FALSE, level.instant_relocation);
5419
5420   TestIfPlayerTouchesBadThing(jx, jy);
5421   TestIfPlayerTouchesCustomElement(jx, jy);
5422
5423   if (IS_CUSTOM_ELEMENT(element))
5424     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5425                                player->index_bit, enter_side);
5426
5427   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5428                                       player->index_bit, enter_side);
5429
5430   if (player->is_switching)
5431   {
5432     /* ensure that relocation while still switching an element does not cause
5433        a new element to be treated as also switched directly after relocation
5434        (this is important for teleporter switches that teleport the player to
5435        a place where another teleporter switch is in the same direction, which
5436        would then incorrectly be treated as immediately switched before the
5437        direction key that caused the switch was released) */
5438
5439     player->switch_x += jx - old_jx;
5440     player->switch_y += jy - old_jy;
5441   }
5442 }
5443
5444 static void Explode(int ex, int ey, int phase, int mode)
5445 {
5446   int x, y;
5447   int last_phase;
5448   int border_element;
5449
5450   // !!! eliminate this variable !!!
5451   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5452
5453   if (game.explosions_delayed)
5454   {
5455     ExplodeField[ex][ey] = mode;
5456     return;
5457   }
5458
5459   if (phase == EX_PHASE_START)          // initialize 'Store[][]' field
5460   {
5461     int center_element = Feld[ex][ey];
5462     int artwork_element, explosion_element;     // set these values later
5463
5464     // remove things displayed in background while burning dynamite
5465     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5466       Back[ex][ey] = 0;
5467
5468     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5469     {
5470       // put moving element to center field (and let it explode there)
5471       center_element = MovingOrBlocked2Element(ex, ey);
5472       RemoveMovingField(ex, ey);
5473       Feld[ex][ey] = center_element;
5474     }
5475
5476     // now "center_element" is finally determined -- set related values now
5477     artwork_element = center_element;           // for custom player artwork
5478     explosion_element = center_element;         // for custom player artwork
5479
5480     if (IS_PLAYER(ex, ey))
5481     {
5482       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5483
5484       artwork_element = stored_player[player_nr].artwork_element;
5485
5486       if (level.use_explosion_element[player_nr])
5487       {
5488         explosion_element = level.explosion_element[player_nr];
5489         artwork_element = explosion_element;
5490       }
5491     }
5492
5493     if (mode == EX_TYPE_NORMAL ||
5494         mode == EX_TYPE_CENTER ||
5495         mode == EX_TYPE_CROSS)
5496       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5497
5498     last_phase = element_info[explosion_element].explosion_delay + 1;
5499
5500     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5501     {
5502       int xx = x - ex + 1;
5503       int yy = y - ey + 1;
5504       int element;
5505
5506       if (!IN_LEV_FIELD(x, y) ||
5507           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5508           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5509         continue;
5510
5511       element = Feld[x][y];
5512
5513       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5514       {
5515         element = MovingOrBlocked2Element(x, y);
5516
5517         if (!IS_EXPLOSION_PROOF(element))
5518           RemoveMovingField(x, y);
5519       }
5520
5521       // indestructible elements can only explode in center (but not flames)
5522       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5523                                            mode == EX_TYPE_BORDER)) ||
5524           element == EL_FLAMES)
5525         continue;
5526
5527       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5528          behaviour, for example when touching a yamyam that explodes to rocks
5529          with active deadly shield, a rock is created under the player !!! */
5530       // (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8)
5531 #if 0
5532       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5533           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5534            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5535 #else
5536       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5537 #endif
5538       {
5539         if (IS_ACTIVE_BOMB(element))
5540         {
5541           // re-activate things under the bomb like gate or penguin
5542           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5543           Back[x][y] = 0;
5544         }
5545
5546         continue;
5547       }
5548
5549       // save walkable background elements while explosion on same tile
5550       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5551           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5552         Back[x][y] = element;
5553
5554       // ignite explodable elements reached by other explosion
5555       if (element == EL_EXPLOSION)
5556         element = Store2[x][y];
5557
5558       if (AmoebaNr[x][y] &&
5559           (element == EL_AMOEBA_FULL ||
5560            element == EL_BD_AMOEBA ||
5561            element == EL_AMOEBA_GROWING))
5562       {
5563         AmoebaCnt[AmoebaNr[x][y]]--;
5564         AmoebaCnt2[AmoebaNr[x][y]]--;
5565       }
5566
5567       RemoveField(x, y);
5568
5569       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5570       {
5571         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5572
5573         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5574
5575         if (PLAYERINFO(ex, ey)->use_murphy)
5576           Store[x][y] = EL_EMPTY;
5577       }
5578
5579       // !!! check this case -- currently needed for rnd_rado_negundo_v,
5580       // !!! levels 015 018 019 020 021 022 023 026 027 028 !!!
5581       else if (ELEM_IS_PLAYER(center_element))
5582         Store[x][y] = EL_EMPTY;
5583       else if (center_element == EL_YAMYAM)
5584         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5585       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5586         Store[x][y] = element_info[center_element].content.e[xx][yy];
5587 #if 1
5588       // needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5589       // (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5590       // otherwise) -- FIX THIS !!!
5591       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5592         Store[x][y] = element_info[element].content.e[1][1];
5593 #else
5594       else if (!CAN_EXPLODE(element))
5595         Store[x][y] = element_info[element].content.e[1][1];
5596 #endif
5597       else
5598         Store[x][y] = EL_EMPTY;
5599
5600       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5601           center_element == EL_AMOEBA_TO_DIAMOND)
5602         Store2[x][y] = element;
5603
5604       Feld[x][y] = EL_EXPLOSION;
5605       GfxElement[x][y] = artwork_element;
5606
5607       ExplodePhase[x][y] = 1;
5608       ExplodeDelay[x][y] = last_phase;
5609
5610       Stop[x][y] = TRUE;
5611     }
5612
5613     if (center_element == EL_YAMYAM)
5614       game.yamyam_content_nr =
5615         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5616
5617     return;
5618   }
5619
5620   if (Stop[ex][ey])
5621     return;
5622
5623   x = ex;
5624   y = ey;
5625
5626   if (phase == 1)
5627     GfxFrame[x][y] = 0;         // restart explosion animation
5628
5629   last_phase = ExplodeDelay[x][y];
5630
5631   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5632
5633   // this can happen if the player leaves an explosion just in time
5634   if (GfxElement[x][y] == EL_UNDEFINED)
5635     GfxElement[x][y] = EL_EMPTY;
5636
5637   border_element = Store2[x][y];
5638   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5639     border_element = StorePlayer[x][y];
5640
5641   if (phase == element_info[border_element].ignition_delay ||
5642       phase == last_phase)
5643   {
5644     boolean border_explosion = FALSE;
5645
5646     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5647         !PLAYER_EXPLOSION_PROTECTED(x, y))
5648     {
5649       KillPlayerUnlessExplosionProtected(x, y);
5650       border_explosion = TRUE;
5651     }
5652     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5653     {
5654       Feld[x][y] = Store2[x][y];
5655       Store2[x][y] = 0;
5656       Bang(x, y);
5657       border_explosion = TRUE;
5658     }
5659     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5660     {
5661       AmoebeUmwandeln(x, y);
5662       Store2[x][y] = 0;
5663       border_explosion = TRUE;
5664     }
5665
5666     // if an element just explodes due to another explosion (chain-reaction),
5667     // do not immediately end the new explosion when it was the last frame of
5668     // the explosion (as it would be done in the following "if"-statement!)
5669     if (border_explosion && phase == last_phase)
5670       return;
5671   }
5672
5673   if (phase == last_phase)
5674   {
5675     int element;
5676
5677     element = Feld[x][y] = Store[x][y];
5678     Store[x][y] = Store2[x][y] = 0;
5679     GfxElement[x][y] = EL_UNDEFINED;
5680
5681     // player can escape from explosions and might therefore be still alive
5682     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5683         element <= EL_PLAYER_IS_EXPLODING_4)
5684     {
5685       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5686       int explosion_element = EL_PLAYER_1 + player_nr;
5687       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5688       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5689
5690       if (level.use_explosion_element[player_nr])
5691         explosion_element = level.explosion_element[player_nr];
5692
5693       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5694                     element_info[explosion_element].content.e[xx][yy]);
5695     }
5696
5697     // restore probably existing indestructible background element
5698     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5699       element = Feld[x][y] = Back[x][y];
5700     Back[x][y] = 0;
5701
5702     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5703     GfxDir[x][y] = MV_NONE;
5704     ChangeDelay[x][y] = 0;
5705     ChangePage[x][y] = -1;
5706
5707     CustomValue[x][y] = 0;
5708
5709     InitField_WithBug2(x, y, FALSE);
5710
5711     TEST_DrawLevelField(x, y);
5712
5713     TestIfElementTouchesCustomElement(x, y);
5714
5715     if (GFX_CRUMBLED(element))
5716       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5717
5718     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5719       StorePlayer[x][y] = 0;
5720
5721     if (ELEM_IS_PLAYER(element))
5722       RelocatePlayer(x, y, element);
5723   }
5724   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5725   {
5726     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5727     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5728
5729     if (phase == delay)
5730       TEST_DrawLevelFieldCrumbled(x, y);
5731
5732     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5733     {
5734       DrawLevelElement(x, y, Back[x][y]);
5735       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5736     }
5737     else if (IS_WALKABLE_UNDER(Back[x][y]))
5738     {
5739       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5740       DrawLevelElementThruMask(x, y, Back[x][y]);
5741     }
5742     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5743       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5744   }
5745 }
5746
5747 static void DynaExplode(int ex, int ey)
5748 {
5749   int i, j;
5750   int dynabomb_element = Feld[ex][ey];
5751   int dynabomb_size = 1;
5752   boolean dynabomb_xl = FALSE;
5753   struct PlayerInfo *player;
5754   static int xy[4][2] =
5755   {
5756     { 0, -1 },
5757     { -1, 0 },
5758     { +1, 0 },
5759     { 0, +1 }
5760   };
5761
5762   if (IS_ACTIVE_BOMB(dynabomb_element))
5763   {
5764     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5765     dynabomb_size = player->dynabomb_size;
5766     dynabomb_xl = player->dynabomb_xl;
5767     player->dynabombs_left++;
5768   }
5769
5770   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5771
5772   for (i = 0; i < NUM_DIRECTIONS; i++)
5773   {
5774     for (j = 1; j <= dynabomb_size; j++)
5775     {
5776       int x = ex + j * xy[i][0];
5777       int y = ey + j * xy[i][1];
5778       int element;
5779
5780       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5781         break;
5782
5783       element = Feld[x][y];
5784
5785       // do not restart explosions of fields with active bombs
5786       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5787         continue;
5788
5789       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5790
5791       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5792           !IS_DIGGABLE(element) && !dynabomb_xl)
5793         break;
5794     }
5795   }
5796 }
5797
5798 void Bang(int x, int y)
5799 {
5800   int element = MovingOrBlocked2Element(x, y);
5801   int explosion_type = EX_TYPE_NORMAL;
5802
5803   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5804   {
5805     struct PlayerInfo *player = PLAYERINFO(x, y);
5806
5807     element = Feld[x][y] = player->initial_element;
5808
5809     if (level.use_explosion_element[player->index_nr])
5810     {
5811       int explosion_element = level.explosion_element[player->index_nr];
5812
5813       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5814         explosion_type = EX_TYPE_CROSS;
5815       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5816         explosion_type = EX_TYPE_CENTER;
5817     }
5818   }
5819
5820   switch (element)
5821   {
5822     case EL_BUG:
5823     case EL_SPACESHIP:
5824     case EL_BD_BUTTERFLY:
5825     case EL_BD_FIREFLY:
5826     case EL_YAMYAM:
5827     case EL_DARK_YAMYAM:
5828     case EL_ROBOT:
5829     case EL_PACMAN:
5830     case EL_MOLE:
5831       RaiseScoreElement(element);
5832       break;
5833
5834     case EL_DYNABOMB_PLAYER_1_ACTIVE:
5835     case EL_DYNABOMB_PLAYER_2_ACTIVE:
5836     case EL_DYNABOMB_PLAYER_3_ACTIVE:
5837     case EL_DYNABOMB_PLAYER_4_ACTIVE:
5838     case EL_DYNABOMB_INCREASE_NUMBER:
5839     case EL_DYNABOMB_INCREASE_SIZE:
5840     case EL_DYNABOMB_INCREASE_POWER:
5841       explosion_type = EX_TYPE_DYNA;
5842       break;
5843
5844     case EL_DC_LANDMINE:
5845       explosion_type = EX_TYPE_CENTER;
5846       break;
5847
5848     case EL_PENGUIN:
5849     case EL_LAMP:
5850     case EL_LAMP_ACTIVE:
5851     case EL_AMOEBA_TO_DIAMOND:
5852       if (!IS_PLAYER(x, y))     // penguin and player may be at same field
5853         explosion_type = EX_TYPE_CENTER;
5854       break;
5855
5856     default:
5857       if (element_info[element].explosion_type == EXPLODES_CROSS)
5858         explosion_type = EX_TYPE_CROSS;
5859       else if (element_info[element].explosion_type == EXPLODES_1X1)
5860         explosion_type = EX_TYPE_CENTER;
5861       break;
5862   }
5863
5864   if (explosion_type == EX_TYPE_DYNA)
5865     DynaExplode(x, y);
5866   else
5867     Explode(x, y, EX_PHASE_START, explosion_type);
5868
5869   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5870 }
5871
5872 static void SplashAcid(int x, int y)
5873 {
5874   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5875       (!IN_LEV_FIELD(x - 1, y - 2) ||
5876        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5877     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5878
5879   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5880       (!IN_LEV_FIELD(x + 1, y - 2) ||
5881        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5882     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5883
5884   PlayLevelSound(x, y, SND_ACID_SPLASHING);
5885 }
5886
5887 static void InitBeltMovement(void)
5888 {
5889   static int belt_base_element[4] =
5890   {
5891     EL_CONVEYOR_BELT_1_LEFT,
5892     EL_CONVEYOR_BELT_2_LEFT,
5893     EL_CONVEYOR_BELT_3_LEFT,
5894     EL_CONVEYOR_BELT_4_LEFT
5895   };
5896   static int belt_base_active_element[4] =
5897   {
5898     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5899     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5900     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5901     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5902   };
5903
5904   int x, y, i, j;
5905
5906   // set frame order for belt animation graphic according to belt direction
5907   for (i = 0; i < NUM_BELTS; i++)
5908   {
5909     int belt_nr = i;
5910
5911     for (j = 0; j < NUM_BELT_PARTS; j++)
5912     {
5913       int element = belt_base_active_element[belt_nr] + j;
5914       int graphic_1 = el2img(element);
5915       int graphic_2 = el2panelimg(element);
5916
5917       if (game.belt_dir[i] == MV_LEFT)
5918       {
5919         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5920         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5921       }
5922       else
5923       {
5924         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5925         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5926       }
5927     }
5928   }
5929
5930   SCAN_PLAYFIELD(x, y)
5931   {
5932     int element = Feld[x][y];
5933
5934     for (i = 0; i < NUM_BELTS; i++)
5935     {
5936       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5937       {
5938         int e_belt_nr = getBeltNrFromBeltElement(element);
5939         int belt_nr = i;
5940
5941         if (e_belt_nr == belt_nr)
5942         {
5943           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5944
5945           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5946         }
5947       }
5948     }
5949   }
5950 }
5951
5952 static void ToggleBeltSwitch(int x, int y)
5953 {
5954   static int belt_base_element[4] =
5955   {
5956     EL_CONVEYOR_BELT_1_LEFT,
5957     EL_CONVEYOR_BELT_2_LEFT,
5958     EL_CONVEYOR_BELT_3_LEFT,
5959     EL_CONVEYOR_BELT_4_LEFT
5960   };
5961   static int belt_base_active_element[4] =
5962   {
5963     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5964     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5965     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5966     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5967   };
5968   static int belt_base_switch_element[4] =
5969   {
5970     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5971     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5972     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5973     EL_CONVEYOR_BELT_4_SWITCH_LEFT
5974   };
5975   static int belt_move_dir[4] =
5976   {
5977     MV_LEFT,
5978     MV_NONE,
5979     MV_RIGHT,
5980     MV_NONE,
5981   };
5982
5983   int element = Feld[x][y];
5984   int belt_nr = getBeltNrFromBeltSwitchElement(element);
5985   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5986   int belt_dir = belt_move_dir[belt_dir_nr];
5987   int xx, yy, i;
5988
5989   if (!IS_BELT_SWITCH(element))
5990     return;
5991
5992   game.belt_dir_nr[belt_nr] = belt_dir_nr;
5993   game.belt_dir[belt_nr] = belt_dir;
5994
5995   if (belt_dir_nr == 3)
5996     belt_dir_nr = 1;
5997
5998   // set frame order for belt animation graphic according to belt direction
5999   for (i = 0; i < NUM_BELT_PARTS; i++)
6000   {
6001     int element = belt_base_active_element[belt_nr] + i;
6002     int graphic_1 = el2img(element);
6003     int graphic_2 = el2panelimg(element);
6004
6005     if (belt_dir == MV_LEFT)
6006     {
6007       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6008       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6009     }
6010     else
6011     {
6012       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6013       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6014     }
6015   }
6016
6017   SCAN_PLAYFIELD(xx, yy)
6018   {
6019     int element = Feld[xx][yy];
6020
6021     if (IS_BELT_SWITCH(element))
6022     {
6023       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6024
6025       if (e_belt_nr == belt_nr)
6026       {
6027         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6028         TEST_DrawLevelField(xx, yy);
6029       }
6030     }
6031     else if (IS_BELT(element) && belt_dir != MV_NONE)
6032     {
6033       int e_belt_nr = getBeltNrFromBeltElement(element);
6034
6035       if (e_belt_nr == belt_nr)
6036       {
6037         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
6038
6039         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6040         TEST_DrawLevelField(xx, yy);
6041       }
6042     }
6043     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6044     {
6045       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6046
6047       if (e_belt_nr == belt_nr)
6048       {
6049         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
6050
6051         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
6052         TEST_DrawLevelField(xx, yy);
6053       }
6054     }
6055   }
6056 }
6057
6058 static void ToggleSwitchgateSwitch(int x, int y)
6059 {
6060   int xx, yy;
6061
6062   game.switchgate_pos = !game.switchgate_pos;
6063
6064   SCAN_PLAYFIELD(xx, yy)
6065   {
6066     int element = Feld[xx][yy];
6067
6068     if (element == EL_SWITCHGATE_SWITCH_UP)
6069     {
6070       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6071       TEST_DrawLevelField(xx, yy);
6072     }
6073     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6074     {
6075       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6076       TEST_DrawLevelField(xx, yy);
6077     }
6078     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6079     {
6080       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6081       TEST_DrawLevelField(xx, yy);
6082     }
6083     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6084     {
6085       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6086       TEST_DrawLevelField(xx, yy);
6087     }
6088     else if (element == EL_SWITCHGATE_OPEN ||
6089              element == EL_SWITCHGATE_OPENING)
6090     {
6091       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6092
6093       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6094     }
6095     else if (element == EL_SWITCHGATE_CLOSED ||
6096              element == EL_SWITCHGATE_CLOSING)
6097     {
6098       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6099
6100       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6101     }
6102   }
6103 }
6104
6105 static int getInvisibleActiveFromInvisibleElement(int element)
6106 {
6107   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6108           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6109           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6110           element);
6111 }
6112
6113 static int getInvisibleFromInvisibleActiveElement(int element)
6114 {
6115   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6116           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6117           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6118           element);
6119 }
6120
6121 static void RedrawAllLightSwitchesAndInvisibleElements(void)
6122 {
6123   int x, y;
6124
6125   SCAN_PLAYFIELD(x, y)
6126   {
6127     int element = Feld[x][y];
6128
6129     if (element == EL_LIGHT_SWITCH &&
6130         game.light_time_left > 0)
6131     {
6132       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6133       TEST_DrawLevelField(x, y);
6134     }
6135     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6136              game.light_time_left == 0)
6137     {
6138       Feld[x][y] = EL_LIGHT_SWITCH;
6139       TEST_DrawLevelField(x, y);
6140     }
6141     else if (element == EL_EMC_DRIPPER &&
6142              game.light_time_left > 0)
6143     {
6144       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6145       TEST_DrawLevelField(x, y);
6146     }
6147     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6148              game.light_time_left == 0)
6149     {
6150       Feld[x][y] = EL_EMC_DRIPPER;
6151       TEST_DrawLevelField(x, y);
6152     }
6153     else if (element == EL_INVISIBLE_STEELWALL ||
6154              element == EL_INVISIBLE_WALL ||
6155              element == EL_INVISIBLE_SAND)
6156     {
6157       if (game.light_time_left > 0)
6158         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6159
6160       TEST_DrawLevelField(x, y);
6161
6162       // uncrumble neighbour fields, if needed
6163       if (element == EL_INVISIBLE_SAND)
6164         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6165     }
6166     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6167              element == EL_INVISIBLE_WALL_ACTIVE ||
6168              element == EL_INVISIBLE_SAND_ACTIVE)
6169     {
6170       if (game.light_time_left == 0)
6171         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6172
6173       TEST_DrawLevelField(x, y);
6174
6175       // re-crumble neighbour fields, if needed
6176       if (element == EL_INVISIBLE_SAND)
6177         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6178     }
6179   }
6180 }
6181
6182 static void RedrawAllInvisibleElementsForLenses(void)
6183 {
6184   int x, y;
6185
6186   SCAN_PLAYFIELD(x, y)
6187   {
6188     int element = Feld[x][y];
6189
6190     if (element == EL_EMC_DRIPPER &&
6191         game.lenses_time_left > 0)
6192     {
6193       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6194       TEST_DrawLevelField(x, y);
6195     }
6196     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6197              game.lenses_time_left == 0)
6198     {
6199       Feld[x][y] = EL_EMC_DRIPPER;
6200       TEST_DrawLevelField(x, y);
6201     }
6202     else if (element == EL_INVISIBLE_STEELWALL ||
6203              element == EL_INVISIBLE_WALL ||
6204              element == EL_INVISIBLE_SAND)
6205     {
6206       if (game.lenses_time_left > 0)
6207         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6208
6209       TEST_DrawLevelField(x, y);
6210
6211       // uncrumble neighbour fields, if needed
6212       if (element == EL_INVISIBLE_SAND)
6213         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6214     }
6215     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6216              element == EL_INVISIBLE_WALL_ACTIVE ||
6217              element == EL_INVISIBLE_SAND_ACTIVE)
6218     {
6219       if (game.lenses_time_left == 0)
6220         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6221
6222       TEST_DrawLevelField(x, y);
6223
6224       // re-crumble neighbour fields, if needed
6225       if (element == EL_INVISIBLE_SAND)
6226         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6227     }
6228   }
6229 }
6230
6231 static void RedrawAllInvisibleElementsForMagnifier(void)
6232 {
6233   int x, y;
6234
6235   SCAN_PLAYFIELD(x, y)
6236   {
6237     int element = Feld[x][y];
6238
6239     if (element == EL_EMC_FAKE_GRASS &&
6240         game.magnify_time_left > 0)
6241     {
6242       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6243       TEST_DrawLevelField(x, y);
6244     }
6245     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6246              game.magnify_time_left == 0)
6247     {
6248       Feld[x][y] = EL_EMC_FAKE_GRASS;
6249       TEST_DrawLevelField(x, y);
6250     }
6251     else if (IS_GATE_GRAY(element) &&
6252              game.magnify_time_left > 0)
6253     {
6254       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6255                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6256                     IS_EM_GATE_GRAY(element) ?
6257                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6258                     IS_EMC_GATE_GRAY(element) ?
6259                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6260                     IS_DC_GATE_GRAY(element) ?
6261                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6262                     element);
6263       TEST_DrawLevelField(x, y);
6264     }
6265     else if (IS_GATE_GRAY_ACTIVE(element) &&
6266              game.magnify_time_left == 0)
6267     {
6268       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6269                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6270                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6271                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6272                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6273                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6274                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6275                     EL_DC_GATE_WHITE_GRAY :
6276                     element);
6277       TEST_DrawLevelField(x, y);
6278     }
6279   }
6280 }
6281
6282 static void ToggleLightSwitch(int x, int y)
6283 {
6284   int element = Feld[x][y];
6285
6286   game.light_time_left =
6287     (element == EL_LIGHT_SWITCH ?
6288      level.time_light * FRAMES_PER_SECOND : 0);
6289
6290   RedrawAllLightSwitchesAndInvisibleElements();
6291 }
6292
6293 static void ActivateTimegateSwitch(int x, int y)
6294 {
6295   int xx, yy;
6296
6297   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6298
6299   SCAN_PLAYFIELD(xx, yy)
6300   {
6301     int element = Feld[xx][yy];
6302
6303     if (element == EL_TIMEGATE_CLOSED ||
6304         element == EL_TIMEGATE_CLOSING)
6305     {
6306       Feld[xx][yy] = EL_TIMEGATE_OPENING;
6307       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6308     }
6309
6310     /*
6311     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6312     {
6313       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6314       TEST_DrawLevelField(xx, yy);
6315     }
6316     */
6317
6318   }
6319
6320   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6321                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6322 }
6323
6324 static void Impact(int x, int y)
6325 {
6326   boolean last_line = (y == lev_fieldy - 1);
6327   boolean object_hit = FALSE;
6328   boolean impact = (last_line || object_hit);
6329   int element = Feld[x][y];
6330   int smashed = EL_STEELWALL;
6331
6332   if (!last_line)       // check if element below was hit
6333   {
6334     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6335       return;
6336
6337     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6338                                          MovDir[x][y + 1] != MV_DOWN ||
6339                                          MovPos[x][y + 1] <= TILEY / 2));
6340
6341     // do not smash moving elements that left the smashed field in time
6342     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6343         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6344       object_hit = FALSE;
6345
6346 #if USE_QUICKSAND_IMPACT_BUGFIX
6347     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6348     {
6349       RemoveMovingField(x, y + 1);
6350       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6351       Feld[x][y + 2] = EL_ROCK;
6352       TEST_DrawLevelField(x, y + 2);
6353
6354       object_hit = TRUE;
6355     }
6356
6357     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6358     {
6359       RemoveMovingField(x, y + 1);
6360       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6361       Feld[x][y + 2] = EL_ROCK;
6362       TEST_DrawLevelField(x, y + 2);
6363
6364       object_hit = TRUE;
6365     }
6366 #endif
6367
6368     if (object_hit)
6369       smashed = MovingOrBlocked2Element(x, y + 1);
6370
6371     impact = (last_line || object_hit);
6372   }
6373
6374   if (!last_line && smashed == EL_ACID) // element falls into acid
6375   {
6376     SplashAcid(x, y + 1);
6377     return;
6378   }
6379
6380   // !!! not sufficient for all cases -- see EL_PEARL below !!!
6381   // only reset graphic animation if graphic really changes after impact
6382   if (impact &&
6383       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6384   {
6385     ResetGfxAnimation(x, y);
6386     TEST_DrawLevelField(x, y);
6387   }
6388
6389   if (impact && CAN_EXPLODE_IMPACT(element))
6390   {
6391     Bang(x, y);
6392     return;
6393   }
6394   else if (impact && element == EL_PEARL &&
6395            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6396   {
6397     ResetGfxAnimation(x, y);
6398
6399     Feld[x][y] = EL_PEARL_BREAKING;
6400     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6401     return;
6402   }
6403   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6404   {
6405     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6406
6407     return;
6408   }
6409
6410   if (impact && element == EL_AMOEBA_DROP)
6411   {
6412     if (object_hit && IS_PLAYER(x, y + 1))
6413       KillPlayerUnlessEnemyProtected(x, y + 1);
6414     else if (object_hit && smashed == EL_PENGUIN)
6415       Bang(x, y + 1);
6416     else
6417     {
6418       Feld[x][y] = EL_AMOEBA_GROWING;
6419       Store[x][y] = EL_AMOEBA_WET;
6420
6421       ResetRandomAnimationValue(x, y);
6422     }
6423     return;
6424   }
6425
6426   if (object_hit)               // check which object was hit
6427   {
6428     if ((CAN_PASS_MAGIC_WALL(element) && 
6429          (smashed == EL_MAGIC_WALL ||
6430           smashed == EL_BD_MAGIC_WALL)) ||
6431         (CAN_PASS_DC_MAGIC_WALL(element) &&
6432          smashed == EL_DC_MAGIC_WALL))
6433     {
6434       int xx, yy;
6435       int activated_magic_wall =
6436         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6437          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6438          EL_DC_MAGIC_WALL_ACTIVE);
6439
6440       // activate magic wall / mill
6441       SCAN_PLAYFIELD(xx, yy)
6442       {
6443         if (Feld[xx][yy] == smashed)
6444           Feld[xx][yy] = activated_magic_wall;
6445       }
6446
6447       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6448       game.magic_wall_active = TRUE;
6449
6450       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6451                             SND_MAGIC_WALL_ACTIVATING :
6452                             smashed == EL_BD_MAGIC_WALL ?
6453                             SND_BD_MAGIC_WALL_ACTIVATING :
6454                             SND_DC_MAGIC_WALL_ACTIVATING));
6455     }
6456
6457     if (IS_PLAYER(x, y + 1))
6458     {
6459       if (CAN_SMASH_PLAYER(element))
6460       {
6461         KillPlayerUnlessEnemyProtected(x, y + 1);
6462         return;
6463       }
6464     }
6465     else if (smashed == EL_PENGUIN)
6466     {
6467       if (CAN_SMASH_PLAYER(element))
6468       {
6469         Bang(x, y + 1);
6470         return;
6471       }
6472     }
6473     else if (element == EL_BD_DIAMOND)
6474     {
6475       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6476       {
6477         Bang(x, y + 1);
6478         return;
6479       }
6480     }
6481     else if (((element == EL_SP_INFOTRON ||
6482                element == EL_SP_ZONK) &&
6483               (smashed == EL_SP_SNIKSNAK ||
6484                smashed == EL_SP_ELECTRON ||
6485                smashed == EL_SP_DISK_ORANGE)) ||
6486              (element == EL_SP_INFOTRON &&
6487               smashed == EL_SP_DISK_YELLOW))
6488     {
6489       Bang(x, y + 1);
6490       return;
6491     }
6492     else if (CAN_SMASH_EVERYTHING(element))
6493     {
6494       if (IS_CLASSIC_ENEMY(smashed) ||
6495           CAN_EXPLODE_SMASHED(smashed))
6496       {
6497         Bang(x, y + 1);
6498         return;
6499       }
6500       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6501       {
6502         if (smashed == EL_LAMP ||
6503             smashed == EL_LAMP_ACTIVE)
6504         {
6505           Bang(x, y + 1);
6506           return;
6507         }
6508         else if (smashed == EL_NUT)
6509         {
6510           Feld[x][y + 1] = EL_NUT_BREAKING;
6511           PlayLevelSound(x, y, SND_NUT_BREAKING);
6512           RaiseScoreElement(EL_NUT);
6513           return;
6514         }
6515         else if (smashed == EL_PEARL)
6516         {
6517           ResetGfxAnimation(x, y);
6518
6519           Feld[x][y + 1] = EL_PEARL_BREAKING;
6520           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6521           return;
6522         }
6523         else if (smashed == EL_DIAMOND)
6524         {
6525           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6526           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6527           return;
6528         }
6529         else if (IS_BELT_SWITCH(smashed))
6530         {
6531           ToggleBeltSwitch(x, y + 1);
6532         }
6533         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6534                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6535                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6536                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6537         {
6538           ToggleSwitchgateSwitch(x, y + 1);
6539         }
6540         else if (smashed == EL_LIGHT_SWITCH ||
6541                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6542         {
6543           ToggleLightSwitch(x, y + 1);
6544         }
6545         else
6546         {
6547           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6548
6549           CheckElementChangeBySide(x, y + 1, smashed, element,
6550                                    CE_SWITCHED, CH_SIDE_TOP);
6551           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6552                                             CH_SIDE_TOP);
6553         }
6554       }
6555       else
6556       {
6557         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6558       }
6559     }
6560   }
6561
6562   // play sound of magic wall / mill
6563   if (!last_line &&
6564       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6565        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6566        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6567   {
6568     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6569       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6570     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6571       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6572     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6573       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6574
6575     return;
6576   }
6577
6578   // play sound of object that hits the ground
6579   if (last_line || object_hit)
6580     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6581 }
6582
6583 static void TurnRoundExt(int x, int y)
6584 {
6585   static struct
6586   {
6587     int dx, dy;
6588   } move_xy[] =
6589   {
6590     {  0,  0 },
6591     { -1,  0 },
6592     { +1,  0 },
6593     {  0,  0 },
6594     {  0, -1 },
6595     {  0,  0 }, { 0, 0 }, { 0, 0 },
6596     {  0, +1 }
6597   };
6598   static struct
6599   {
6600     int left, right, back;
6601   } turn[] =
6602   {
6603     { 0,        0,              0        },
6604     { MV_DOWN,  MV_UP,          MV_RIGHT },
6605     { MV_UP,    MV_DOWN,        MV_LEFT  },
6606     { 0,        0,              0        },
6607     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6608     { 0,        0,              0        },
6609     { 0,        0,              0        },
6610     { 0,        0,              0        },
6611     { MV_RIGHT, MV_LEFT,        MV_UP    }
6612   };
6613
6614   int element = Feld[x][y];
6615   int move_pattern = element_info[element].move_pattern;
6616
6617   int old_move_dir = MovDir[x][y];
6618   int left_dir  = turn[old_move_dir].left;
6619   int right_dir = turn[old_move_dir].right;
6620   int back_dir  = turn[old_move_dir].back;
6621
6622   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6623   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6624   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6625   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6626
6627   int left_x  = x + left_dx,  left_y  = y + left_dy;
6628   int right_x = x + right_dx, right_y = y + right_dy;
6629   int move_x  = x + move_dx,  move_y  = y + move_dy;
6630
6631   int xx, yy;
6632
6633   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6634   {
6635     TestIfBadThingTouchesOtherBadThing(x, y);
6636
6637     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6638       MovDir[x][y] = right_dir;
6639     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6640       MovDir[x][y] = left_dir;
6641
6642     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6643       MovDelay[x][y] = 9;
6644     else if (element == EL_BD_BUTTERFLY)     // && MovDir[x][y] == left_dir)
6645       MovDelay[x][y] = 1;
6646   }
6647   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6648   {
6649     TestIfBadThingTouchesOtherBadThing(x, y);
6650
6651     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6652       MovDir[x][y] = left_dir;
6653     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6654       MovDir[x][y] = right_dir;
6655
6656     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6657       MovDelay[x][y] = 9;
6658     else if (element == EL_BD_FIREFLY)      // && MovDir[x][y] == right_dir)
6659       MovDelay[x][y] = 1;
6660   }
6661   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6662   {
6663     TestIfBadThingTouchesOtherBadThing(x, y);
6664
6665     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6666       MovDir[x][y] = left_dir;
6667     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6668       MovDir[x][y] = right_dir;
6669
6670     if (MovDir[x][y] != old_move_dir)
6671       MovDelay[x][y] = 9;
6672   }
6673   else if (element == EL_YAMYAM)
6674   {
6675     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6676     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6677
6678     if (can_turn_left && can_turn_right)
6679       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6680     else if (can_turn_left)
6681       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6682     else if (can_turn_right)
6683       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6684     else
6685       MovDir[x][y] = back_dir;
6686
6687     MovDelay[x][y] = 16 + 16 * RND(3);
6688   }
6689   else if (element == EL_DARK_YAMYAM)
6690   {
6691     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6692                                                          left_x, left_y);
6693     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6694                                                          right_x, right_y);
6695
6696     if (can_turn_left && can_turn_right)
6697       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6698     else if (can_turn_left)
6699       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6700     else if (can_turn_right)
6701       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6702     else
6703       MovDir[x][y] = back_dir;
6704
6705     MovDelay[x][y] = 16 + 16 * RND(3);
6706   }
6707   else if (element == EL_PACMAN)
6708   {
6709     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6710     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6711
6712     if (can_turn_left && can_turn_right)
6713       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6714     else if (can_turn_left)
6715       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6716     else if (can_turn_right)
6717       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6718     else
6719       MovDir[x][y] = back_dir;
6720
6721     MovDelay[x][y] = 6 + RND(40);
6722   }
6723   else if (element == EL_PIG)
6724   {
6725     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6726     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6727     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6728     boolean should_turn_left, should_turn_right, should_move_on;
6729     int rnd_value = 24;
6730     int rnd = RND(rnd_value);
6731
6732     should_turn_left = (can_turn_left &&
6733                         (!can_move_on ||
6734                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6735                                                    y + back_dy + left_dy)));
6736     should_turn_right = (can_turn_right &&
6737                          (!can_move_on ||
6738                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6739                                                     y + back_dy + right_dy)));
6740     should_move_on = (can_move_on &&
6741                       (!can_turn_left ||
6742                        !can_turn_right ||
6743                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6744                                                  y + move_dy + left_dy) ||
6745                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6746                                                  y + move_dy + right_dy)));
6747
6748     if (should_turn_left || should_turn_right || should_move_on)
6749     {
6750       if (should_turn_left && should_turn_right && should_move_on)
6751         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6752                         rnd < 2 * rnd_value / 3 ? right_dir :
6753                         old_move_dir);
6754       else if (should_turn_left && should_turn_right)
6755         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6756       else if (should_turn_left && should_move_on)
6757         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6758       else if (should_turn_right && should_move_on)
6759         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6760       else if (should_turn_left)
6761         MovDir[x][y] = left_dir;
6762       else if (should_turn_right)
6763         MovDir[x][y] = right_dir;
6764       else if (should_move_on)
6765         MovDir[x][y] = old_move_dir;
6766     }
6767     else if (can_move_on && rnd > rnd_value / 8)
6768       MovDir[x][y] = old_move_dir;
6769     else if (can_turn_left && can_turn_right)
6770       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6771     else if (can_turn_left && rnd > rnd_value / 8)
6772       MovDir[x][y] = left_dir;
6773     else if (can_turn_right && rnd > rnd_value/8)
6774       MovDir[x][y] = right_dir;
6775     else
6776       MovDir[x][y] = back_dir;
6777
6778     xx = x + move_xy[MovDir[x][y]].dx;
6779     yy = y + move_xy[MovDir[x][y]].dy;
6780
6781     if (!IN_LEV_FIELD(xx, yy) ||
6782         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6783       MovDir[x][y] = old_move_dir;
6784
6785     MovDelay[x][y] = 0;
6786   }
6787   else if (element == EL_DRAGON)
6788   {
6789     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6790     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6791     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6792     int rnd_value = 24;
6793     int rnd = RND(rnd_value);
6794
6795     if (can_move_on && rnd > rnd_value / 8)
6796       MovDir[x][y] = old_move_dir;
6797     else if (can_turn_left && can_turn_right)
6798       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6799     else if (can_turn_left && rnd > rnd_value / 8)
6800       MovDir[x][y] = left_dir;
6801     else if (can_turn_right && rnd > rnd_value / 8)
6802       MovDir[x][y] = right_dir;
6803     else
6804       MovDir[x][y] = back_dir;
6805
6806     xx = x + move_xy[MovDir[x][y]].dx;
6807     yy = y + move_xy[MovDir[x][y]].dy;
6808
6809     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6810       MovDir[x][y] = old_move_dir;
6811
6812     MovDelay[x][y] = 0;
6813   }
6814   else if (element == EL_MOLE)
6815   {
6816     boolean can_move_on =
6817       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6818                             IS_AMOEBOID(Feld[move_x][move_y]) ||
6819                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6820     if (!can_move_on)
6821     {
6822       boolean can_turn_left =
6823         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6824                               IS_AMOEBOID(Feld[left_x][left_y])));
6825
6826       boolean can_turn_right =
6827         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6828                               IS_AMOEBOID(Feld[right_x][right_y])));
6829
6830       if (can_turn_left && can_turn_right)
6831         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6832       else if (can_turn_left)
6833         MovDir[x][y] = left_dir;
6834       else
6835         MovDir[x][y] = right_dir;
6836     }
6837
6838     if (MovDir[x][y] != old_move_dir)
6839       MovDelay[x][y] = 9;
6840   }
6841   else if (element == EL_BALLOON)
6842   {
6843     MovDir[x][y] = game.wind_direction;
6844     MovDelay[x][y] = 0;
6845   }
6846   else if (element == EL_SPRING)
6847   {
6848     if (MovDir[x][y] & MV_HORIZONTAL)
6849     {
6850       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6851           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6852       {
6853         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6854         ResetGfxAnimation(move_x, move_y);
6855         TEST_DrawLevelField(move_x, move_y);
6856
6857         MovDir[x][y] = back_dir;
6858       }
6859       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6860                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6861         MovDir[x][y] = MV_NONE;
6862     }
6863
6864     MovDelay[x][y] = 0;
6865   }
6866   else if (element == EL_ROBOT ||
6867            element == EL_SATELLITE ||
6868            element == EL_PENGUIN ||
6869            element == EL_EMC_ANDROID)
6870   {
6871     int attr_x = -1, attr_y = -1;
6872
6873     if (game.all_players_gone)
6874     {
6875       attr_x = game.exit_x;
6876       attr_y = game.exit_y;
6877     }
6878     else
6879     {
6880       int i;
6881
6882       for (i = 0; i < MAX_PLAYERS; i++)
6883       {
6884         struct PlayerInfo *player = &stored_player[i];
6885         int jx = player->jx, jy = player->jy;
6886
6887         if (!player->active)
6888           continue;
6889
6890         if (attr_x == -1 ||
6891             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6892         {
6893           attr_x = jx;
6894           attr_y = jy;
6895         }
6896       }
6897     }
6898
6899     if (element == EL_ROBOT &&
6900         game.robot_wheel_x >= 0 &&
6901         game.robot_wheel_y >= 0 &&
6902         (Feld[game.robot_wheel_x][game.robot_wheel_y] == EL_ROBOT_WHEEL_ACTIVE ||
6903          game.engine_version < VERSION_IDENT(3,1,0,0)))
6904     {
6905       attr_x = game.robot_wheel_x;
6906       attr_y = game.robot_wheel_y;
6907     }
6908
6909     if (element == EL_PENGUIN)
6910     {
6911       int i;
6912       static int xy[4][2] =
6913       {
6914         { 0, -1 },
6915         { -1, 0 },
6916         { +1, 0 },
6917         { 0, +1 }
6918       };
6919
6920       for (i = 0; i < NUM_DIRECTIONS; i++)
6921       {
6922         int ex = x + xy[i][0];
6923         int ey = y + xy[i][1];
6924
6925         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6926                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6927                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6928                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6929         {
6930           attr_x = ex;
6931           attr_y = ey;
6932           break;
6933         }
6934       }
6935     }
6936
6937     MovDir[x][y] = MV_NONE;
6938     if (attr_x < x)
6939       MovDir[x][y] |= (game.all_players_gone ? MV_RIGHT : MV_LEFT);
6940     else if (attr_x > x)
6941       MovDir[x][y] |= (game.all_players_gone ? MV_LEFT : MV_RIGHT);
6942     if (attr_y < y)
6943       MovDir[x][y] |= (game.all_players_gone ? MV_DOWN : MV_UP);
6944     else if (attr_y > y)
6945       MovDir[x][y] |= (game.all_players_gone ? MV_UP : MV_DOWN);
6946
6947     if (element == EL_ROBOT)
6948     {
6949       int newx, newy;
6950
6951       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6952         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6953       Moving2Blocked(x, y, &newx, &newy);
6954
6955       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6956         MovDelay[x][y] = 8 + 8 * !RND(3);
6957       else
6958         MovDelay[x][y] = 16;
6959     }
6960     else if (element == EL_PENGUIN)
6961     {
6962       int newx, newy;
6963
6964       MovDelay[x][y] = 1;
6965
6966       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6967       {
6968         boolean first_horiz = RND(2);
6969         int new_move_dir = MovDir[x][y];
6970
6971         MovDir[x][y] =
6972           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6973         Moving2Blocked(x, y, &newx, &newy);
6974
6975         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6976           return;
6977
6978         MovDir[x][y] =
6979           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6980         Moving2Blocked(x, y, &newx, &newy);
6981
6982         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6983           return;
6984
6985         MovDir[x][y] = old_move_dir;
6986         return;
6987       }
6988     }
6989     else if (element == EL_SATELLITE)
6990     {
6991       int newx, newy;
6992
6993       MovDelay[x][y] = 1;
6994
6995       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6996       {
6997         boolean first_horiz = RND(2);
6998         int new_move_dir = MovDir[x][y];
6999
7000         MovDir[x][y] =
7001           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7002         Moving2Blocked(x, y, &newx, &newy);
7003
7004         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7005           return;
7006
7007         MovDir[x][y] =
7008           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7009         Moving2Blocked(x, y, &newx, &newy);
7010
7011         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7012           return;
7013
7014         MovDir[x][y] = old_move_dir;
7015         return;
7016       }
7017     }
7018     else if (element == EL_EMC_ANDROID)
7019     {
7020       static int check_pos[16] =
7021       {
7022         -1,             //  0 => (invalid)
7023         7,              //  1 => MV_LEFT
7024         3,              //  2 => MV_RIGHT
7025         -1,             //  3 => (invalid)
7026         1,              //  4 =>            MV_UP
7027         0,              //  5 => MV_LEFT  | MV_UP
7028         2,              //  6 => MV_RIGHT | MV_UP
7029         -1,             //  7 => (invalid)
7030         5,              //  8 =>            MV_DOWN
7031         6,              //  9 => MV_LEFT  | MV_DOWN
7032         4,              // 10 => MV_RIGHT | MV_DOWN
7033         -1,             // 11 => (invalid)
7034         -1,             // 12 => (invalid)
7035         -1,             // 13 => (invalid)
7036         -1,             // 14 => (invalid)
7037         -1,             // 15 => (invalid)
7038       };
7039       static struct
7040       {
7041         int dx, dy;
7042         int dir;
7043       } check_xy[8] =
7044       {
7045         { -1, -1,       MV_LEFT  | MV_UP   },
7046         {  0, -1,                  MV_UP   },
7047         { +1, -1,       MV_RIGHT | MV_UP   },
7048         { +1,  0,       MV_RIGHT           },
7049         { +1, +1,       MV_RIGHT | MV_DOWN },
7050         {  0, +1,                  MV_DOWN },
7051         { -1, +1,       MV_LEFT  | MV_DOWN },
7052         { -1,  0,       MV_LEFT            },
7053       };
7054       int start_pos, check_order;
7055       boolean can_clone = FALSE;
7056       int i;
7057
7058       // check if there is any free field around current position
7059       for (i = 0; i < 8; i++)
7060       {
7061         int newx = x + check_xy[i].dx;
7062         int newy = y + check_xy[i].dy;
7063
7064         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7065         {
7066           can_clone = TRUE;
7067
7068           break;
7069         }
7070       }
7071
7072       if (can_clone)            // randomly find an element to clone
7073       {
7074         can_clone = FALSE;
7075
7076         start_pos = check_pos[RND(8)];
7077         check_order = (RND(2) ? -1 : +1);
7078
7079         for (i = 0; i < 8; i++)
7080         {
7081           int pos_raw = start_pos + i * check_order;
7082           int pos = (pos_raw + 8) % 8;
7083           int newx = x + check_xy[pos].dx;
7084           int newy = y + check_xy[pos].dy;
7085
7086           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7087           {
7088             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7089             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7090
7091             Store[x][y] = Feld[newx][newy];
7092
7093             can_clone = TRUE;
7094
7095             break;
7096           }
7097         }
7098       }
7099
7100       if (can_clone)            // randomly find a direction to move
7101       {
7102         can_clone = FALSE;
7103
7104         start_pos = check_pos[RND(8)];
7105         check_order = (RND(2) ? -1 : +1);
7106
7107         for (i = 0; i < 8; i++)
7108         {
7109           int pos_raw = start_pos + i * check_order;
7110           int pos = (pos_raw + 8) % 8;
7111           int newx = x + check_xy[pos].dx;
7112           int newy = y + check_xy[pos].dy;
7113           int new_move_dir = check_xy[pos].dir;
7114
7115           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7116           {
7117             MovDir[x][y] = new_move_dir;
7118             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7119
7120             can_clone = TRUE;
7121
7122             break;
7123           }
7124         }
7125       }
7126
7127       if (can_clone)            // cloning and moving successful
7128         return;
7129
7130       // cannot clone -- try to move towards player
7131
7132       start_pos = check_pos[MovDir[x][y] & 0x0f];
7133       check_order = (RND(2) ? -1 : +1);
7134
7135       for (i = 0; i < 3; i++)
7136       {
7137         // first check start_pos, then previous/next or (next/previous) pos
7138         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7139         int pos = (pos_raw + 8) % 8;
7140         int newx = x + check_xy[pos].dx;
7141         int newy = y + check_xy[pos].dy;
7142         int new_move_dir = check_xy[pos].dir;
7143
7144         if (IS_PLAYER(newx, newy))
7145           break;
7146
7147         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7148         {
7149           MovDir[x][y] = new_move_dir;
7150           MovDelay[x][y] = level.android_move_time * 8 + 1;
7151
7152           break;
7153         }
7154       }
7155     }
7156   }
7157   else if (move_pattern == MV_TURNING_LEFT ||
7158            move_pattern == MV_TURNING_RIGHT ||
7159            move_pattern == MV_TURNING_LEFT_RIGHT ||
7160            move_pattern == MV_TURNING_RIGHT_LEFT ||
7161            move_pattern == MV_TURNING_RANDOM ||
7162            move_pattern == MV_ALL_DIRECTIONS)
7163   {
7164     boolean can_turn_left =
7165       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7166     boolean can_turn_right =
7167       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7168
7169     if (element_info[element].move_stepsize == 0)       // "not moving"
7170       return;
7171
7172     if (move_pattern == MV_TURNING_LEFT)
7173       MovDir[x][y] = left_dir;
7174     else if (move_pattern == MV_TURNING_RIGHT)
7175       MovDir[x][y] = right_dir;
7176     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7177       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7178     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7179       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7180     else if (move_pattern == MV_TURNING_RANDOM)
7181       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7182                       can_turn_right && !can_turn_left ? right_dir :
7183                       RND(2) ? left_dir : right_dir);
7184     else if (can_turn_left && can_turn_right)
7185       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7186     else if (can_turn_left)
7187       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7188     else if (can_turn_right)
7189       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7190     else
7191       MovDir[x][y] = back_dir;
7192
7193     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7194   }
7195   else if (move_pattern == MV_HORIZONTAL ||
7196            move_pattern == MV_VERTICAL)
7197   {
7198     if (move_pattern & old_move_dir)
7199       MovDir[x][y] = back_dir;
7200     else if (move_pattern == MV_HORIZONTAL)
7201       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7202     else if (move_pattern == MV_VERTICAL)
7203       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7204
7205     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7206   }
7207   else if (move_pattern & MV_ANY_DIRECTION)
7208   {
7209     MovDir[x][y] = move_pattern;
7210     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7211   }
7212   else if (move_pattern & MV_WIND_DIRECTION)
7213   {
7214     MovDir[x][y] = game.wind_direction;
7215     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7216   }
7217   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7218   {
7219     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7220       MovDir[x][y] = left_dir;
7221     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7222       MovDir[x][y] = right_dir;
7223
7224     if (MovDir[x][y] != old_move_dir)
7225       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7226   }
7227   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7228   {
7229     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7230       MovDir[x][y] = right_dir;
7231     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7232       MovDir[x][y] = left_dir;
7233
7234     if (MovDir[x][y] != old_move_dir)
7235       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7236   }
7237   else if (move_pattern == MV_TOWARDS_PLAYER ||
7238            move_pattern == MV_AWAY_FROM_PLAYER)
7239   {
7240     int attr_x = -1, attr_y = -1;
7241     int newx, newy;
7242     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7243
7244     if (game.all_players_gone)
7245     {
7246       attr_x = game.exit_x;
7247       attr_y = game.exit_y;
7248     }
7249     else
7250     {
7251       int i;
7252
7253       for (i = 0; i < MAX_PLAYERS; i++)
7254       {
7255         struct PlayerInfo *player = &stored_player[i];
7256         int jx = player->jx, jy = player->jy;
7257
7258         if (!player->active)
7259           continue;
7260
7261         if (attr_x == -1 ||
7262             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7263         {
7264           attr_x = jx;
7265           attr_y = jy;
7266         }
7267       }
7268     }
7269
7270     MovDir[x][y] = MV_NONE;
7271     if (attr_x < x)
7272       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7273     else if (attr_x > x)
7274       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7275     if (attr_y < y)
7276       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7277     else if (attr_y > y)
7278       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7279
7280     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7281
7282     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7283     {
7284       boolean first_horiz = RND(2);
7285       int new_move_dir = MovDir[x][y];
7286
7287       if (element_info[element].move_stepsize == 0)     // "not moving"
7288       {
7289         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7290         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7291
7292         return;
7293       }
7294
7295       MovDir[x][y] =
7296         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7297       Moving2Blocked(x, y, &newx, &newy);
7298
7299       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7300         return;
7301
7302       MovDir[x][y] =
7303         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7304       Moving2Blocked(x, y, &newx, &newy);
7305
7306       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7307         return;
7308
7309       MovDir[x][y] = old_move_dir;
7310     }
7311   }
7312   else if (move_pattern == MV_WHEN_PUSHED ||
7313            move_pattern == MV_WHEN_DROPPED)
7314   {
7315     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7316       MovDir[x][y] = MV_NONE;
7317
7318     MovDelay[x][y] = 0;
7319   }
7320   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7321   {
7322     static int test_xy[7][2] =
7323     {
7324       { 0, -1 },
7325       { -1, 0 },
7326       { +1, 0 },
7327       { 0, +1 },
7328       { 0, -1 },
7329       { -1, 0 },
7330       { +1, 0 },
7331     };
7332     static int test_dir[7] =
7333     {
7334       MV_UP,
7335       MV_LEFT,
7336       MV_RIGHT,
7337       MV_DOWN,
7338       MV_UP,
7339       MV_LEFT,
7340       MV_RIGHT,
7341     };
7342     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7343     int move_preference = -1000000;     // start with very low preference
7344     int new_move_dir = MV_NONE;
7345     int start_test = RND(4);
7346     int i;
7347
7348     for (i = 0; i < NUM_DIRECTIONS; i++)
7349     {
7350       int move_dir = test_dir[start_test + i];
7351       int move_dir_preference;
7352
7353       xx = x + test_xy[start_test + i][0];
7354       yy = y + test_xy[start_test + i][1];
7355
7356       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7357           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7358       {
7359         new_move_dir = move_dir;
7360
7361         break;
7362       }
7363
7364       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7365         continue;
7366
7367       move_dir_preference = -1 * RunnerVisit[xx][yy];
7368       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7369         move_dir_preference = PlayerVisit[xx][yy];
7370
7371       if (move_dir_preference > move_preference)
7372       {
7373         // prefer field that has not been visited for the longest time
7374         move_preference = move_dir_preference;
7375         new_move_dir = move_dir;
7376       }
7377       else if (move_dir_preference == move_preference &&
7378                move_dir == old_move_dir)
7379       {
7380         // prefer last direction when all directions are preferred equally
7381         move_preference = move_dir_preference;
7382         new_move_dir = move_dir;
7383       }
7384     }
7385
7386     MovDir[x][y] = new_move_dir;
7387     if (old_move_dir != new_move_dir)
7388       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7389   }
7390 }
7391
7392 static void TurnRound(int x, int y)
7393 {
7394   int direction = MovDir[x][y];
7395
7396   TurnRoundExt(x, y);
7397
7398   GfxDir[x][y] = MovDir[x][y];
7399
7400   if (direction != MovDir[x][y])
7401     GfxFrame[x][y] = 0;
7402
7403   if (MovDelay[x][y])
7404     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7405
7406   ResetGfxFrame(x, y);
7407 }
7408
7409 static boolean JustBeingPushed(int x, int y)
7410 {
7411   int i;
7412
7413   for (i = 0; i < MAX_PLAYERS; i++)
7414   {
7415     struct PlayerInfo *player = &stored_player[i];
7416
7417     if (player->active && player->is_pushing && player->MovPos)
7418     {
7419       int next_jx = player->jx + (player->jx - player->last_jx);
7420       int next_jy = player->jy + (player->jy - player->last_jy);
7421
7422       if (x == next_jx && y == next_jy)
7423         return TRUE;
7424     }
7425   }
7426
7427   return FALSE;
7428 }
7429
7430 static void StartMoving(int x, int y)
7431 {
7432   boolean started_moving = FALSE;       // some elements can fall _and_ move
7433   int element = Feld[x][y];
7434
7435   if (Stop[x][y])
7436     return;
7437
7438   if (MovDelay[x][y] == 0)
7439     GfxAction[x][y] = ACTION_DEFAULT;
7440
7441   if (CAN_FALL(element) && y < lev_fieldy - 1)
7442   {
7443     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7444         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7445       if (JustBeingPushed(x, y))
7446         return;
7447
7448     if (element == EL_QUICKSAND_FULL)
7449     {
7450       if (IS_FREE(x, y + 1))
7451       {
7452         InitMovingField(x, y, MV_DOWN);
7453         started_moving = TRUE;
7454
7455         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7456 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7457         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7458           Store[x][y] = EL_ROCK;
7459 #else
7460         Store[x][y] = EL_ROCK;
7461 #endif
7462
7463         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7464       }
7465       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7466       {
7467         if (!MovDelay[x][y])
7468         {
7469           MovDelay[x][y] = TILEY + 1;
7470
7471           ResetGfxAnimation(x, y);
7472           ResetGfxAnimation(x, y + 1);
7473         }
7474
7475         if (MovDelay[x][y])
7476         {
7477           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7478           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7479
7480           MovDelay[x][y]--;
7481           if (MovDelay[x][y])
7482             return;
7483         }
7484
7485         Feld[x][y] = EL_QUICKSAND_EMPTY;
7486         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7487         Store[x][y + 1] = Store[x][y];
7488         Store[x][y] = 0;
7489
7490         PlayLevelSoundAction(x, y, ACTION_FILLING);
7491       }
7492       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7493       {
7494         if (!MovDelay[x][y])
7495         {
7496           MovDelay[x][y] = TILEY + 1;
7497
7498           ResetGfxAnimation(x, y);
7499           ResetGfxAnimation(x, y + 1);
7500         }
7501
7502         if (MovDelay[x][y])
7503         {
7504           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7505           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7506
7507           MovDelay[x][y]--;
7508           if (MovDelay[x][y])
7509             return;
7510         }
7511
7512         Feld[x][y] = EL_QUICKSAND_EMPTY;
7513         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7514         Store[x][y + 1] = Store[x][y];
7515         Store[x][y] = 0;
7516
7517         PlayLevelSoundAction(x, y, ACTION_FILLING);
7518       }
7519     }
7520     else if (element == EL_QUICKSAND_FAST_FULL)
7521     {
7522       if (IS_FREE(x, y + 1))
7523       {
7524         InitMovingField(x, y, MV_DOWN);
7525         started_moving = TRUE;
7526
7527         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7528 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7529         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7530           Store[x][y] = EL_ROCK;
7531 #else
7532         Store[x][y] = EL_ROCK;
7533 #endif
7534
7535         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7536       }
7537       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7538       {
7539         if (!MovDelay[x][y])
7540         {
7541           MovDelay[x][y] = TILEY + 1;
7542
7543           ResetGfxAnimation(x, y);
7544           ResetGfxAnimation(x, y + 1);
7545         }
7546
7547         if (MovDelay[x][y])
7548         {
7549           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7550           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7551
7552           MovDelay[x][y]--;
7553           if (MovDelay[x][y])
7554             return;
7555         }
7556
7557         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7558         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7559         Store[x][y + 1] = Store[x][y];
7560         Store[x][y] = 0;
7561
7562         PlayLevelSoundAction(x, y, ACTION_FILLING);
7563       }
7564       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7565       {
7566         if (!MovDelay[x][y])
7567         {
7568           MovDelay[x][y] = TILEY + 1;
7569
7570           ResetGfxAnimation(x, y);
7571           ResetGfxAnimation(x, y + 1);
7572         }
7573
7574         if (MovDelay[x][y])
7575         {
7576           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7577           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7578
7579           MovDelay[x][y]--;
7580           if (MovDelay[x][y])
7581             return;
7582         }
7583
7584         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7585         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7586         Store[x][y + 1] = Store[x][y];
7587         Store[x][y] = 0;
7588
7589         PlayLevelSoundAction(x, y, ACTION_FILLING);
7590       }
7591     }
7592     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7593              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7594     {
7595       InitMovingField(x, y, MV_DOWN);
7596       started_moving = TRUE;
7597
7598       Feld[x][y] = EL_QUICKSAND_FILLING;
7599       Store[x][y] = element;
7600
7601       PlayLevelSoundAction(x, y, ACTION_FILLING);
7602     }
7603     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7604              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7605     {
7606       InitMovingField(x, y, MV_DOWN);
7607       started_moving = TRUE;
7608
7609       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7610       Store[x][y] = element;
7611
7612       PlayLevelSoundAction(x, y, ACTION_FILLING);
7613     }
7614     else if (element == EL_MAGIC_WALL_FULL)
7615     {
7616       if (IS_FREE(x, y + 1))
7617       {
7618         InitMovingField(x, y, MV_DOWN);
7619         started_moving = TRUE;
7620
7621         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7622         Store[x][y] = EL_CHANGED(Store[x][y]);
7623       }
7624       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7625       {
7626         if (!MovDelay[x][y])
7627           MovDelay[x][y] = TILEY / 4 + 1;
7628
7629         if (MovDelay[x][y])
7630         {
7631           MovDelay[x][y]--;
7632           if (MovDelay[x][y])
7633             return;
7634         }
7635
7636         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7637         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7638         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7639         Store[x][y] = 0;
7640       }
7641     }
7642     else if (element == EL_BD_MAGIC_WALL_FULL)
7643     {
7644       if (IS_FREE(x, y + 1))
7645       {
7646         InitMovingField(x, y, MV_DOWN);
7647         started_moving = TRUE;
7648
7649         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7650         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7651       }
7652       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7653       {
7654         if (!MovDelay[x][y])
7655           MovDelay[x][y] = TILEY / 4 + 1;
7656
7657         if (MovDelay[x][y])
7658         {
7659           MovDelay[x][y]--;
7660           if (MovDelay[x][y])
7661             return;
7662         }
7663
7664         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7665         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7666         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7667         Store[x][y] = 0;
7668       }
7669     }
7670     else if (element == EL_DC_MAGIC_WALL_FULL)
7671     {
7672       if (IS_FREE(x, y + 1))
7673       {
7674         InitMovingField(x, y, MV_DOWN);
7675         started_moving = TRUE;
7676
7677         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7678         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7679       }
7680       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7681       {
7682         if (!MovDelay[x][y])
7683           MovDelay[x][y] = TILEY / 4 + 1;
7684
7685         if (MovDelay[x][y])
7686         {
7687           MovDelay[x][y]--;
7688           if (MovDelay[x][y])
7689             return;
7690         }
7691
7692         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7693         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7694         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7695         Store[x][y] = 0;
7696       }
7697     }
7698     else if ((CAN_PASS_MAGIC_WALL(element) &&
7699               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7700                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7701              (CAN_PASS_DC_MAGIC_WALL(element) &&
7702               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7703
7704     {
7705       InitMovingField(x, y, MV_DOWN);
7706       started_moving = TRUE;
7707
7708       Feld[x][y] =
7709         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7710          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7711          EL_DC_MAGIC_WALL_FILLING);
7712       Store[x][y] = element;
7713     }
7714     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7715     {
7716       SplashAcid(x, y + 1);
7717
7718       InitMovingField(x, y, MV_DOWN);
7719       started_moving = TRUE;
7720
7721       Store[x][y] = EL_ACID;
7722     }
7723     else if (
7724              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7725               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7726              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7727               CAN_FALL(element) && WasJustFalling[x][y] &&
7728               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7729
7730              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7731               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7732               (Feld[x][y + 1] == EL_BLOCKED)))
7733     {
7734       /* this is needed for a special case not covered by calling "Impact()"
7735          from "ContinueMoving()": if an element moves to a tile directly below
7736          another element which was just falling on that tile (which was empty
7737          in the previous frame), the falling element above would just stop
7738          instead of smashing the element below (in previous version, the above
7739          element was just checked for "moving" instead of "falling", resulting
7740          in incorrect smashes caused by horizontal movement of the above
7741          element; also, the case of the player being the element to smash was
7742          simply not covered here... :-/ ) */
7743
7744       CheckCollision[x][y] = 0;
7745       CheckImpact[x][y] = 0;
7746
7747       Impact(x, y);
7748     }
7749     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7750     {
7751       if (MovDir[x][y] == MV_NONE)
7752       {
7753         InitMovingField(x, y, MV_DOWN);
7754         started_moving = TRUE;
7755       }
7756     }
7757     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7758     {
7759       if (WasJustFalling[x][y]) // prevent animation from being restarted
7760         MovDir[x][y] = MV_DOWN;
7761
7762       InitMovingField(x, y, MV_DOWN);
7763       started_moving = TRUE;
7764     }
7765     else if (element == EL_AMOEBA_DROP)
7766     {
7767       Feld[x][y] = EL_AMOEBA_GROWING;
7768       Store[x][y] = EL_AMOEBA_WET;
7769     }
7770     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7771               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7772              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7773              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7774     {
7775       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7776                                 (IS_FREE(x - 1, y + 1) ||
7777                                  Feld[x - 1][y + 1] == EL_ACID));
7778       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7779                                 (IS_FREE(x + 1, y + 1) ||
7780                                  Feld[x + 1][y + 1] == EL_ACID));
7781       boolean can_fall_any  = (can_fall_left || can_fall_right);
7782       boolean can_fall_both = (can_fall_left && can_fall_right);
7783       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7784
7785       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7786       {
7787         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7788           can_fall_right = FALSE;
7789         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7790           can_fall_left = FALSE;
7791         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7792           can_fall_right = FALSE;
7793         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7794           can_fall_left = FALSE;
7795
7796         can_fall_any  = (can_fall_left || can_fall_right);
7797         can_fall_both = FALSE;
7798       }
7799
7800       if (can_fall_both)
7801       {
7802         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7803           can_fall_right = FALSE;       // slip down on left side
7804         else
7805           can_fall_left = !(can_fall_right = RND(2));
7806
7807         can_fall_both = FALSE;
7808       }
7809
7810       if (can_fall_any)
7811       {
7812         // if not determined otherwise, prefer left side for slipping down
7813         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7814         started_moving = TRUE;
7815       }
7816     }
7817     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7818     {
7819       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
7820       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7821       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7822       int belt_dir = game.belt_dir[belt_nr];
7823
7824       if ((belt_dir == MV_LEFT  && left_is_free) ||
7825           (belt_dir == MV_RIGHT && right_is_free))
7826       {
7827         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7828
7829         InitMovingField(x, y, belt_dir);
7830         started_moving = TRUE;
7831
7832         Pushed[x][y] = TRUE;
7833         Pushed[nextx][y] = TRUE;
7834
7835         GfxAction[x][y] = ACTION_DEFAULT;
7836       }
7837       else
7838       {
7839         MovDir[x][y] = 0;       // if element was moving, stop it
7840       }
7841     }
7842   }
7843
7844   // not "else if" because of elements that can fall and move (EL_SPRING)
7845   if (CAN_MOVE(element) && !started_moving)
7846   {
7847     int move_pattern = element_info[element].move_pattern;
7848     int newx, newy;
7849
7850     Moving2Blocked(x, y, &newx, &newy);
7851
7852     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7853       return;
7854
7855     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7856         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7857     {
7858       WasJustMoving[x][y] = 0;
7859       CheckCollision[x][y] = 0;
7860
7861       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7862
7863       if (Feld[x][y] != element)        // element has changed
7864         return;
7865     }
7866
7867     if (!MovDelay[x][y])        // start new movement phase
7868     {
7869       // all objects that can change their move direction after each step
7870       // (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall
7871
7872       if (element != EL_YAMYAM &&
7873           element != EL_DARK_YAMYAM &&
7874           element != EL_PACMAN &&
7875           !(move_pattern & MV_ANY_DIRECTION) &&
7876           move_pattern != MV_TURNING_LEFT &&
7877           move_pattern != MV_TURNING_RIGHT &&
7878           move_pattern != MV_TURNING_LEFT_RIGHT &&
7879           move_pattern != MV_TURNING_RIGHT_LEFT &&
7880           move_pattern != MV_TURNING_RANDOM)
7881       {
7882         TurnRound(x, y);
7883
7884         if (MovDelay[x][y] && (element == EL_BUG ||
7885                                element == EL_SPACESHIP ||
7886                                element == EL_SP_SNIKSNAK ||
7887                                element == EL_SP_ELECTRON ||
7888                                element == EL_MOLE))
7889           TEST_DrawLevelField(x, y);
7890       }
7891     }
7892
7893     if (MovDelay[x][y])         // wait some time before next movement
7894     {
7895       MovDelay[x][y]--;
7896
7897       if (element == EL_ROBOT ||
7898           element == EL_YAMYAM ||
7899           element == EL_DARK_YAMYAM)
7900       {
7901         DrawLevelElementAnimationIfNeeded(x, y, element);
7902         PlayLevelSoundAction(x, y, ACTION_WAITING);
7903       }
7904       else if (element == EL_SP_ELECTRON)
7905         DrawLevelElementAnimationIfNeeded(x, y, element);
7906       else if (element == EL_DRAGON)
7907       {
7908         int i;
7909         int dir = MovDir[x][y];
7910         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7911         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
7912         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
7913                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
7914                        dir == MV_UP     ? IMG_FLAMES_1_UP :
7915                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7916         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7917
7918         GfxAction[x][y] = ACTION_ATTACKING;
7919
7920         if (IS_PLAYER(x, y))
7921           DrawPlayerField(x, y);
7922         else
7923           TEST_DrawLevelField(x, y);
7924
7925         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7926
7927         for (i = 1; i <= 3; i++)
7928         {
7929           int xx = x + i * dx;
7930           int yy = y + i * dy;
7931           int sx = SCREENX(xx);
7932           int sy = SCREENY(yy);
7933           int flame_graphic = graphic + (i - 1);
7934
7935           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7936             break;
7937
7938           if (MovDelay[x][y])
7939           {
7940             int flamed = MovingOrBlocked2Element(xx, yy);
7941
7942             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7943               Bang(xx, yy);
7944             else
7945               RemoveMovingField(xx, yy);
7946
7947             ChangeDelay[xx][yy] = 0;
7948
7949             Feld[xx][yy] = EL_FLAMES;
7950
7951             if (IN_SCR_FIELD(sx, sy))
7952             {
7953               TEST_DrawLevelFieldCrumbled(xx, yy);
7954               DrawGraphic(sx, sy, flame_graphic, frame);
7955             }
7956           }
7957           else
7958           {
7959             if (Feld[xx][yy] == EL_FLAMES)
7960               Feld[xx][yy] = EL_EMPTY;
7961             TEST_DrawLevelField(xx, yy);
7962           }
7963         }
7964       }
7965
7966       if (MovDelay[x][y])       // element still has to wait some time
7967       {
7968         PlayLevelSoundAction(x, y, ACTION_WAITING);
7969
7970         return;
7971       }
7972     }
7973
7974     // now make next step
7975
7976     Moving2Blocked(x, y, &newx, &newy); // get next screen position
7977
7978     if (DONT_COLLIDE_WITH(element) &&
7979         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7980         !PLAYER_ENEMY_PROTECTED(newx, newy))
7981     {
7982       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7983
7984       return;
7985     }
7986
7987     else if (CAN_MOVE_INTO_ACID(element) &&
7988              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7989              !IS_MV_DIAGONAL(MovDir[x][y]) &&
7990              (MovDir[x][y] == MV_DOWN ||
7991               game.engine_version >= VERSION_IDENT(3,1,0,0)))
7992     {
7993       SplashAcid(newx, newy);
7994       Store[x][y] = EL_ACID;
7995     }
7996     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7997     {
7998       if (Feld[newx][newy] == EL_EXIT_OPEN ||
7999           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
8000           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
8001           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8002       {
8003         RemoveField(x, y);
8004         TEST_DrawLevelField(x, y);
8005
8006         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8007         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8008           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8009
8010         game.friends_still_needed--;
8011         if (!game.friends_still_needed &&
8012             !game.GameOver &&
8013             game.all_players_gone)
8014           LevelSolved();
8015
8016         return;
8017       }
8018       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
8019       {
8020         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8021           TEST_DrawLevelField(newx, newy);
8022         else
8023           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8024       }
8025       else if (!IS_FREE(newx, newy))
8026       {
8027         GfxAction[x][y] = ACTION_WAITING;
8028
8029         if (IS_PLAYER(x, y))
8030           DrawPlayerField(x, y);
8031         else
8032           TEST_DrawLevelField(x, y);
8033
8034         return;
8035       }
8036     }
8037     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8038     {
8039       if (IS_FOOD_PIG(Feld[newx][newy]))
8040       {
8041         if (IS_MOVING(newx, newy))
8042           RemoveMovingField(newx, newy);
8043         else
8044         {
8045           Feld[newx][newy] = EL_EMPTY;
8046           TEST_DrawLevelField(newx, newy);
8047         }
8048
8049         PlayLevelSound(x, y, SND_PIG_DIGGING);
8050       }
8051       else if (!IS_FREE(newx, newy))
8052       {
8053         if (IS_PLAYER(x, y))
8054           DrawPlayerField(x, y);
8055         else
8056           TEST_DrawLevelField(x, y);
8057
8058         return;
8059       }
8060     }
8061     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8062     {
8063       if (Store[x][y] != EL_EMPTY)
8064       {
8065         boolean can_clone = FALSE;
8066         int xx, yy;
8067
8068         // check if element to clone is still there
8069         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8070         {
8071           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8072           {
8073             can_clone = TRUE;
8074
8075             break;
8076           }
8077         }
8078
8079         // cannot clone or target field not free anymore -- do not clone
8080         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8081           Store[x][y] = EL_EMPTY;
8082       }
8083
8084       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8085       {
8086         if (IS_MV_DIAGONAL(MovDir[x][y]))
8087         {
8088           int diagonal_move_dir = MovDir[x][y];
8089           int stored = Store[x][y];
8090           int change_delay = 8;
8091           int graphic;
8092
8093           // android is moving diagonally
8094
8095           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8096
8097           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8098           GfxElement[x][y] = EL_EMC_ANDROID;
8099           GfxAction[x][y] = ACTION_SHRINKING;
8100           GfxDir[x][y] = diagonal_move_dir;
8101           ChangeDelay[x][y] = change_delay;
8102
8103           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8104                                    GfxDir[x][y]);
8105
8106           DrawLevelGraphicAnimation(x, y, graphic);
8107           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8108
8109           if (Feld[newx][newy] == EL_ACID)
8110           {
8111             SplashAcid(newx, newy);
8112
8113             return;
8114           }
8115
8116           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8117
8118           Store[newx][newy] = EL_EMC_ANDROID;
8119           GfxElement[newx][newy] = EL_EMC_ANDROID;
8120           GfxAction[newx][newy] = ACTION_GROWING;
8121           GfxDir[newx][newy] = diagonal_move_dir;
8122           ChangeDelay[newx][newy] = change_delay;
8123
8124           graphic = el_act_dir2img(GfxElement[newx][newy],
8125                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8126
8127           DrawLevelGraphicAnimation(newx, newy, graphic);
8128           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8129
8130           return;
8131         }
8132         else
8133         {
8134           Feld[newx][newy] = EL_EMPTY;
8135           TEST_DrawLevelField(newx, newy);
8136
8137           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8138         }
8139       }
8140       else if (!IS_FREE(newx, newy))
8141       {
8142         return;
8143       }
8144     }
8145     else if (IS_CUSTOM_ELEMENT(element) &&
8146              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8147     {
8148       if (!DigFieldByCE(newx, newy, element))
8149         return;
8150
8151       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8152       {
8153         RunnerVisit[x][y] = FrameCounter;
8154         PlayerVisit[x][y] /= 8;         // expire player visit path
8155       }
8156     }
8157     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8158     {
8159       if (!IS_FREE(newx, newy))
8160       {
8161         if (IS_PLAYER(x, y))
8162           DrawPlayerField(x, y);
8163         else
8164           TEST_DrawLevelField(x, y);
8165
8166         return;
8167       }
8168       else
8169       {
8170         boolean wanna_flame = !RND(10);
8171         int dx = newx - x, dy = newy - y;
8172         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8173         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8174         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8175                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8176         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8177                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8178
8179         if ((wanna_flame ||
8180              IS_CLASSIC_ENEMY(element1) ||
8181              IS_CLASSIC_ENEMY(element2)) &&
8182             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8183             element1 != EL_FLAMES && element2 != EL_FLAMES)
8184         {
8185           ResetGfxAnimation(x, y);
8186           GfxAction[x][y] = ACTION_ATTACKING;
8187
8188           if (IS_PLAYER(x, y))
8189             DrawPlayerField(x, y);
8190           else
8191             TEST_DrawLevelField(x, y);
8192
8193           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8194
8195           MovDelay[x][y] = 50;
8196
8197           Feld[newx][newy] = EL_FLAMES;
8198           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8199             Feld[newx1][newy1] = EL_FLAMES;
8200           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8201             Feld[newx2][newy2] = EL_FLAMES;
8202
8203           return;
8204         }
8205       }
8206     }
8207     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8208              Feld[newx][newy] == EL_DIAMOND)
8209     {
8210       if (IS_MOVING(newx, newy))
8211         RemoveMovingField(newx, newy);
8212       else
8213       {
8214         Feld[newx][newy] = EL_EMPTY;
8215         TEST_DrawLevelField(newx, newy);
8216       }
8217
8218       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8219     }
8220     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8221              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8222     {
8223       if (AmoebaNr[newx][newy])
8224       {
8225         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8226         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8227             Feld[newx][newy] == EL_BD_AMOEBA)
8228           AmoebaCnt[AmoebaNr[newx][newy]]--;
8229       }
8230
8231       if (IS_MOVING(newx, newy))
8232       {
8233         RemoveMovingField(newx, newy);
8234       }
8235       else
8236       {
8237         Feld[newx][newy] = EL_EMPTY;
8238         TEST_DrawLevelField(newx, newy);
8239       }
8240
8241       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8242     }
8243     else if ((element == EL_PACMAN || element == EL_MOLE)
8244              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8245     {
8246       if (AmoebaNr[newx][newy])
8247       {
8248         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8249         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8250             Feld[newx][newy] == EL_BD_AMOEBA)
8251           AmoebaCnt[AmoebaNr[newx][newy]]--;
8252       }
8253
8254       if (element == EL_MOLE)
8255       {
8256         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8257         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8258
8259         ResetGfxAnimation(x, y);
8260         GfxAction[x][y] = ACTION_DIGGING;
8261         TEST_DrawLevelField(x, y);
8262
8263         MovDelay[newx][newy] = 0;       // start amoeba shrinking delay
8264
8265         return;                         // wait for shrinking amoeba
8266       }
8267       else      // element == EL_PACMAN
8268       {
8269         Feld[newx][newy] = EL_EMPTY;
8270         TEST_DrawLevelField(newx, newy);
8271         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8272       }
8273     }
8274     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8275              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8276               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8277     {
8278       // wait for shrinking amoeba to completely disappear
8279       return;
8280     }
8281     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8282     {
8283       // object was running against a wall
8284
8285       TurnRound(x, y);
8286
8287       if (GFX_ELEMENT(element) != EL_SAND)     // !!! FIX THIS (crumble) !!!
8288         DrawLevelElementAnimation(x, y, element);
8289
8290       if (DONT_TOUCH(element))
8291         TestIfBadThingTouchesPlayer(x, y);
8292
8293       return;
8294     }
8295
8296     InitMovingField(x, y, MovDir[x][y]);
8297
8298     PlayLevelSoundAction(x, y, ACTION_MOVING);
8299   }
8300
8301   if (MovDir[x][y])
8302     ContinueMoving(x, y);
8303 }
8304
8305 void ContinueMoving(int x, int y)
8306 {
8307   int element = Feld[x][y];
8308   struct ElementInfo *ei = &element_info[element];
8309   int direction = MovDir[x][y];
8310   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8311   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8312   int newx = x + dx, newy = y + dy;
8313   int stored = Store[x][y];
8314   int stored_new = Store[newx][newy];
8315   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8316   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8317   boolean last_line = (newy == lev_fieldy - 1);
8318
8319   MovPos[x][y] += getElementMoveStepsize(x, y);
8320
8321   if (pushed_by_player) // special case: moving object pushed by player
8322     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8323
8324   if (ABS(MovPos[x][y]) < TILEX)
8325   {
8326     TEST_DrawLevelField(x, y);
8327
8328     return;     // element is still moving
8329   }
8330
8331   // element reached destination field
8332
8333   Feld[x][y] = EL_EMPTY;
8334   Feld[newx][newy] = element;
8335   MovPos[x][y] = 0;     // force "not moving" for "crumbled sand"
8336
8337   if (Store[x][y] == EL_ACID)   // element is moving into acid pool
8338   {
8339     element = Feld[newx][newy] = EL_ACID;
8340   }
8341   else if (element == EL_MOLE)
8342   {
8343     Feld[x][y] = EL_SAND;
8344
8345     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8346   }
8347   else if (element == EL_QUICKSAND_FILLING)
8348   {
8349     element = Feld[newx][newy] = get_next_element(element);
8350     Store[newx][newy] = Store[x][y];
8351   }
8352   else if (element == EL_QUICKSAND_EMPTYING)
8353   {
8354     Feld[x][y] = get_next_element(element);
8355     element = Feld[newx][newy] = Store[x][y];
8356   }
8357   else if (element == EL_QUICKSAND_FAST_FILLING)
8358   {
8359     element = Feld[newx][newy] = get_next_element(element);
8360     Store[newx][newy] = Store[x][y];
8361   }
8362   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8363   {
8364     Feld[x][y] = get_next_element(element);
8365     element = Feld[newx][newy] = Store[x][y];
8366   }
8367   else if (element == EL_MAGIC_WALL_FILLING)
8368   {
8369     element = Feld[newx][newy] = get_next_element(element);
8370     if (!game.magic_wall_active)
8371       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8372     Store[newx][newy] = Store[x][y];
8373   }
8374   else if (element == EL_MAGIC_WALL_EMPTYING)
8375   {
8376     Feld[x][y] = get_next_element(element);
8377     if (!game.magic_wall_active)
8378       Feld[x][y] = EL_MAGIC_WALL_DEAD;
8379     element = Feld[newx][newy] = Store[x][y];
8380
8381     InitField(newx, newy, FALSE);
8382   }
8383   else if (element == EL_BD_MAGIC_WALL_FILLING)
8384   {
8385     element = Feld[newx][newy] = get_next_element(element);
8386     if (!game.magic_wall_active)
8387       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8388     Store[newx][newy] = Store[x][y];
8389   }
8390   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8391   {
8392     Feld[x][y] = get_next_element(element);
8393     if (!game.magic_wall_active)
8394       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8395     element = Feld[newx][newy] = Store[x][y];
8396
8397     InitField(newx, newy, FALSE);
8398   }
8399   else if (element == EL_DC_MAGIC_WALL_FILLING)
8400   {
8401     element = Feld[newx][newy] = get_next_element(element);
8402     if (!game.magic_wall_active)
8403       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8404     Store[newx][newy] = Store[x][y];
8405   }
8406   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8407   {
8408     Feld[x][y] = get_next_element(element);
8409     if (!game.magic_wall_active)
8410       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8411     element = Feld[newx][newy] = Store[x][y];
8412
8413     InitField(newx, newy, FALSE);
8414   }
8415   else if (element == EL_AMOEBA_DROPPING)
8416   {
8417     Feld[x][y] = get_next_element(element);
8418     element = Feld[newx][newy] = Store[x][y];
8419   }
8420   else if (element == EL_SOKOBAN_OBJECT)
8421   {
8422     if (Back[x][y])
8423       Feld[x][y] = Back[x][y];
8424
8425     if (Back[newx][newy])
8426       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8427
8428     Back[x][y] = Back[newx][newy] = 0;
8429   }
8430
8431   Store[x][y] = EL_EMPTY;
8432   MovPos[x][y] = 0;
8433   MovDir[x][y] = 0;
8434   MovDelay[x][y] = 0;
8435
8436   MovDelay[newx][newy] = 0;
8437
8438   if (CAN_CHANGE_OR_HAS_ACTION(element))
8439   {
8440     // copy element change control values to new field
8441     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8442     ChangePage[newx][newy]  = ChangePage[x][y];
8443     ChangeCount[newx][newy] = ChangeCount[x][y];
8444     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8445   }
8446
8447   CustomValue[newx][newy] = CustomValue[x][y];
8448
8449   ChangeDelay[x][y] = 0;
8450   ChangePage[x][y] = -1;
8451   ChangeCount[x][y] = 0;
8452   ChangeEvent[x][y] = -1;
8453
8454   CustomValue[x][y] = 0;
8455
8456   // copy animation control values to new field
8457   GfxFrame[newx][newy]  = GfxFrame[x][y];
8458   GfxRandom[newx][newy] = GfxRandom[x][y];      // keep same random value
8459   GfxAction[newx][newy] = GfxAction[x][y];      // keep action one frame
8460   GfxDir[newx][newy]    = GfxDir[x][y];         // keep element direction
8461
8462   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8463
8464   // some elements can leave other elements behind after moving
8465   if (ei->move_leave_element != EL_EMPTY &&
8466       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8467       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8468   {
8469     int move_leave_element = ei->move_leave_element;
8470
8471     // this makes it possible to leave the removed element again
8472     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8473       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8474
8475     Feld[x][y] = move_leave_element;
8476
8477     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8478       MovDir[x][y] = direction;
8479
8480     InitField(x, y, FALSE);
8481
8482     if (GFX_CRUMBLED(Feld[x][y]))
8483       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8484
8485     if (ELEM_IS_PLAYER(move_leave_element))
8486       RelocatePlayer(x, y, move_leave_element);
8487   }
8488
8489   // do this after checking for left-behind element
8490   ResetGfxAnimation(x, y);      // reset animation values for old field
8491
8492   if (!CAN_MOVE(element) ||
8493       (CAN_FALL(element) && direction == MV_DOWN &&
8494        (element == EL_SPRING ||
8495         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8496         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8497     GfxDir[x][y] = MovDir[newx][newy] = 0;
8498
8499   TEST_DrawLevelField(x, y);
8500   TEST_DrawLevelField(newx, newy);
8501
8502   Stop[newx][newy] = TRUE;      // ignore this element until the next frame
8503
8504   // prevent pushed element from moving on in pushed direction
8505   if (pushed_by_player && CAN_MOVE(element) &&
8506       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8507       !(element_info[element].move_pattern & direction))
8508     TurnRound(newx, newy);
8509
8510   // prevent elements on conveyor belt from moving on in last direction
8511   if (pushed_by_conveyor && CAN_FALL(element) &&
8512       direction & MV_HORIZONTAL)
8513     MovDir[newx][newy] = 0;
8514
8515   if (!pushed_by_player)
8516   {
8517     int nextx = newx + dx, nexty = newy + dy;
8518     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8519
8520     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8521
8522     if (CAN_FALL(element) && direction == MV_DOWN)
8523       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8524
8525     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8526       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8527
8528     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8529       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8530   }
8531
8532   if (DONT_TOUCH(element))      // object may be nasty to player or others
8533   {
8534     TestIfBadThingTouchesPlayer(newx, newy);
8535     TestIfBadThingTouchesFriend(newx, newy);
8536
8537     if (!IS_CUSTOM_ELEMENT(element))
8538       TestIfBadThingTouchesOtherBadThing(newx, newy);
8539   }
8540   else if (element == EL_PENGUIN)
8541     TestIfFriendTouchesBadThing(newx, newy);
8542
8543   if (DONT_GET_HIT_BY(element))
8544   {
8545     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8546   }
8547
8548   // give the player one last chance (one more frame) to move away
8549   if (CAN_FALL(element) && direction == MV_DOWN &&
8550       (last_line || (!IS_FREE(x, newy + 1) &&
8551                      (!IS_PLAYER(x, newy + 1) ||
8552                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8553     Impact(x, newy);
8554
8555   if (pushed_by_player && !game.use_change_when_pushing_bug)
8556   {
8557     int push_side = MV_DIR_OPPOSITE(direction);
8558     struct PlayerInfo *player = PLAYERINFO(x, y);
8559
8560     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8561                                player->index_bit, push_side);
8562     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8563                                         player->index_bit, push_side);
8564   }
8565
8566   if (element == EL_EMC_ANDROID && pushed_by_player)    // make another move
8567     MovDelay[newx][newy] = 1;
8568
8569   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8570
8571   TestIfElementTouchesCustomElement(x, y);      // empty or new element
8572   TestIfElementHitsCustomElement(newx, newy, direction);
8573   TestIfPlayerTouchesCustomElement(newx, newy);
8574   TestIfElementTouchesCustomElement(newx, newy);
8575
8576   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8577       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8578     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8579                              MV_DIR_OPPOSITE(direction));
8580 }
8581
8582 int AmoebeNachbarNr(int ax, int ay)
8583 {
8584   int i;
8585   int element = Feld[ax][ay];
8586   int group_nr = 0;
8587   static int xy[4][2] =
8588   {
8589     { 0, -1 },
8590     { -1, 0 },
8591     { +1, 0 },
8592     { 0, +1 }
8593   };
8594
8595   for (i = 0; i < NUM_DIRECTIONS; i++)
8596   {
8597     int x = ax + xy[i][0];
8598     int y = ay + xy[i][1];
8599
8600     if (!IN_LEV_FIELD(x, y))
8601       continue;
8602
8603     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8604       group_nr = AmoebaNr[x][y];
8605   }
8606
8607   return group_nr;
8608 }
8609
8610 static void AmoebenVereinigen(int ax, int ay)
8611 {
8612   int i, x, y, xx, yy;
8613   int new_group_nr = AmoebaNr[ax][ay];
8614   static int xy[4][2] =
8615   {
8616     { 0, -1 },
8617     { -1, 0 },
8618     { +1, 0 },
8619     { 0, +1 }
8620   };
8621
8622   if (new_group_nr == 0)
8623     return;
8624
8625   for (i = 0; i < NUM_DIRECTIONS; i++)
8626   {
8627     x = ax + xy[i][0];
8628     y = ay + xy[i][1];
8629
8630     if (!IN_LEV_FIELD(x, y))
8631       continue;
8632
8633     if ((Feld[x][y] == EL_AMOEBA_FULL ||
8634          Feld[x][y] == EL_BD_AMOEBA ||
8635          Feld[x][y] == EL_AMOEBA_DEAD) &&
8636         AmoebaNr[x][y] != new_group_nr)
8637     {
8638       int old_group_nr = AmoebaNr[x][y];
8639
8640       if (old_group_nr == 0)
8641         return;
8642
8643       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8644       AmoebaCnt[old_group_nr] = 0;
8645       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8646       AmoebaCnt2[old_group_nr] = 0;
8647
8648       SCAN_PLAYFIELD(xx, yy)
8649       {
8650         if (AmoebaNr[xx][yy] == old_group_nr)
8651           AmoebaNr[xx][yy] = new_group_nr;
8652       }
8653     }
8654   }
8655 }
8656
8657 void AmoebeUmwandeln(int ax, int ay)
8658 {
8659   int i, x, y;
8660
8661   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8662   {
8663     int group_nr = AmoebaNr[ax][ay];
8664
8665 #ifdef DEBUG
8666     if (group_nr == 0)
8667     {
8668       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8669       printf("AmoebeUmwandeln(): This should never happen!\n");
8670       return;
8671     }
8672 #endif
8673
8674     SCAN_PLAYFIELD(x, y)
8675     {
8676       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8677       {
8678         AmoebaNr[x][y] = 0;
8679         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8680       }
8681     }
8682
8683     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8684                             SND_AMOEBA_TURNING_TO_GEM :
8685                             SND_AMOEBA_TURNING_TO_ROCK));
8686     Bang(ax, ay);
8687   }
8688   else
8689   {
8690     static int xy[4][2] =
8691     {
8692       { 0, -1 },
8693       { -1, 0 },
8694       { +1, 0 },
8695       { 0, +1 }
8696     };
8697
8698     for (i = 0; i < NUM_DIRECTIONS; i++)
8699     {
8700       x = ax + xy[i][0];
8701       y = ay + xy[i][1];
8702
8703       if (!IN_LEV_FIELD(x, y))
8704         continue;
8705
8706       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8707       {
8708         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8709                               SND_AMOEBA_TURNING_TO_GEM :
8710                               SND_AMOEBA_TURNING_TO_ROCK));
8711         Bang(x, y);
8712       }
8713     }
8714   }
8715 }
8716
8717 static void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8718 {
8719   int x, y;
8720   int group_nr = AmoebaNr[ax][ay];
8721   boolean done = FALSE;
8722
8723 #ifdef DEBUG
8724   if (group_nr == 0)
8725   {
8726     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8727     printf("AmoebeUmwandelnBD(): This should never happen!\n");
8728     return;
8729   }
8730 #endif
8731
8732   SCAN_PLAYFIELD(x, y)
8733   {
8734     if (AmoebaNr[x][y] == group_nr &&
8735         (Feld[x][y] == EL_AMOEBA_DEAD ||
8736          Feld[x][y] == EL_BD_AMOEBA ||
8737          Feld[x][y] == EL_AMOEBA_GROWING))
8738     {
8739       AmoebaNr[x][y] = 0;
8740       Feld[x][y] = new_element;
8741       InitField(x, y, FALSE);
8742       TEST_DrawLevelField(x, y);
8743       done = TRUE;
8744     }
8745   }
8746
8747   if (done)
8748     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8749                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8750                             SND_BD_AMOEBA_TURNING_TO_GEM));
8751 }
8752
8753 static void AmoebeWaechst(int x, int y)
8754 {
8755   static unsigned int sound_delay = 0;
8756   static unsigned int sound_delay_value = 0;
8757
8758   if (!MovDelay[x][y])          // start new growing cycle
8759   {
8760     MovDelay[x][y] = 7;
8761
8762     if (DelayReached(&sound_delay, sound_delay_value))
8763     {
8764       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8765       sound_delay_value = 30;
8766     }
8767   }
8768
8769   if (MovDelay[x][y])           // wait some time before growing bigger
8770   {
8771     MovDelay[x][y]--;
8772     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8773     {
8774       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8775                                            6 - MovDelay[x][y]);
8776
8777       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8778     }
8779
8780     if (!MovDelay[x][y])
8781     {
8782       Feld[x][y] = Store[x][y];
8783       Store[x][y] = 0;
8784       TEST_DrawLevelField(x, y);
8785     }
8786   }
8787 }
8788
8789 static void AmoebaDisappearing(int x, int y)
8790 {
8791   static unsigned int sound_delay = 0;
8792   static unsigned int sound_delay_value = 0;
8793
8794   if (!MovDelay[x][y])          // start new shrinking cycle
8795   {
8796     MovDelay[x][y] = 7;
8797
8798     if (DelayReached(&sound_delay, sound_delay_value))
8799       sound_delay_value = 30;
8800   }
8801
8802   if (MovDelay[x][y])           // wait some time before shrinking
8803   {
8804     MovDelay[x][y]--;
8805     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8806     {
8807       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8808                                            6 - MovDelay[x][y]);
8809
8810       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8811     }
8812
8813     if (!MovDelay[x][y])
8814     {
8815       Feld[x][y] = EL_EMPTY;
8816       TEST_DrawLevelField(x, y);
8817
8818       // don't let mole enter this field in this cycle;
8819       // (give priority to objects falling to this field from above)
8820       Stop[x][y] = TRUE;
8821     }
8822   }
8823 }
8824
8825 static void AmoebeAbleger(int ax, int ay)
8826 {
8827   int i;
8828   int element = Feld[ax][ay];
8829   int graphic = el2img(element);
8830   int newax = ax, neway = ay;
8831   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8832   static int xy[4][2] =
8833   {
8834     { 0, -1 },
8835     { -1, 0 },
8836     { +1, 0 },
8837     { 0, +1 }
8838   };
8839
8840   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8841   {
8842     Feld[ax][ay] = EL_AMOEBA_DEAD;
8843     TEST_DrawLevelField(ax, ay);
8844     return;
8845   }
8846
8847   if (IS_ANIMATED(graphic))
8848     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8849
8850   if (!MovDelay[ax][ay])        // start making new amoeba field
8851     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8852
8853   if (MovDelay[ax][ay])         // wait some time before making new amoeba
8854   {
8855     MovDelay[ax][ay]--;
8856     if (MovDelay[ax][ay])
8857       return;
8858   }
8859
8860   if (can_drop)                 // EL_AMOEBA_WET or EL_EMC_DRIPPER
8861   {
8862     int start = RND(4);
8863     int x = ax + xy[start][0];
8864     int y = ay + xy[start][1];
8865
8866     if (!IN_LEV_FIELD(x, y))
8867       return;
8868
8869     if (IS_FREE(x, y) ||
8870         CAN_GROW_INTO(Feld[x][y]) ||
8871         Feld[x][y] == EL_QUICKSAND_EMPTY ||
8872         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8873     {
8874       newax = x;
8875       neway = y;
8876     }
8877
8878     if (newax == ax && neway == ay)
8879       return;
8880   }
8881   else                          // normal or "filled" (BD style) amoeba
8882   {
8883     int start = RND(4);
8884     boolean waiting_for_player = FALSE;
8885
8886     for (i = 0; i < NUM_DIRECTIONS; i++)
8887     {
8888       int j = (start + i) % 4;
8889       int x = ax + xy[j][0];
8890       int y = ay + xy[j][1];
8891
8892       if (!IN_LEV_FIELD(x, y))
8893         continue;
8894
8895       if (IS_FREE(x, y) ||
8896           CAN_GROW_INTO(Feld[x][y]) ||
8897           Feld[x][y] == EL_QUICKSAND_EMPTY ||
8898           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8899       {
8900         newax = x;
8901         neway = y;
8902         break;
8903       }
8904       else if (IS_PLAYER(x, y))
8905         waiting_for_player = TRUE;
8906     }
8907
8908     if (newax == ax && neway == ay)             // amoeba cannot grow
8909     {
8910       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8911       {
8912         Feld[ax][ay] = EL_AMOEBA_DEAD;
8913         TEST_DrawLevelField(ax, ay);
8914         AmoebaCnt[AmoebaNr[ax][ay]]--;
8915
8916         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   // amoeba is completely dead
8917         {
8918           if (element == EL_AMOEBA_FULL)
8919             AmoebeUmwandeln(ax, ay);
8920           else if (element == EL_BD_AMOEBA)
8921             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8922         }
8923       }
8924       return;
8925     }
8926     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8927     {
8928       // amoeba gets larger by growing in some direction
8929
8930       int new_group_nr = AmoebaNr[ax][ay];
8931
8932 #ifdef DEBUG
8933   if (new_group_nr == 0)
8934   {
8935     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8936     printf("AmoebeAbleger(): This should never happen!\n");
8937     return;
8938   }
8939 #endif
8940
8941       AmoebaNr[newax][neway] = new_group_nr;
8942       AmoebaCnt[new_group_nr]++;
8943       AmoebaCnt2[new_group_nr]++;
8944
8945       // if amoeba touches other amoeba(s) after growing, unify them
8946       AmoebenVereinigen(newax, neway);
8947
8948       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8949       {
8950         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8951         return;
8952       }
8953     }
8954   }
8955
8956   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8957       (neway == lev_fieldy - 1 && newax != ax))
8958   {
8959     Feld[newax][neway] = EL_AMOEBA_GROWING;     // creation of new amoeba
8960     Store[newax][neway] = element;
8961   }
8962   else if (neway == ay || element == EL_EMC_DRIPPER)
8963   {
8964     Feld[newax][neway] = EL_AMOEBA_DROP;        // drop left/right of amoeba
8965
8966     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8967   }
8968   else
8969   {
8970     InitMovingField(ax, ay, MV_DOWN);           // drop dripping from amoeba
8971     Feld[ax][ay] = EL_AMOEBA_DROPPING;
8972     Store[ax][ay] = EL_AMOEBA_DROP;
8973     ContinueMoving(ax, ay);
8974     return;
8975   }
8976
8977   TEST_DrawLevelField(newax, neway);
8978 }
8979
8980 static void Life(int ax, int ay)
8981 {
8982   int x1, y1, x2, y2;
8983   int life_time = 40;
8984   int element = Feld[ax][ay];
8985   int graphic = el2img(element);
8986   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8987                          level.biomaze);
8988   boolean changed = FALSE;
8989
8990   if (IS_ANIMATED(graphic))
8991     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8992
8993   if (Stop[ax][ay])
8994     return;
8995
8996   if (!MovDelay[ax][ay])        // start new "game of life" cycle
8997     MovDelay[ax][ay] = life_time;
8998
8999   if (MovDelay[ax][ay])         // wait some time before next cycle
9000   {
9001     MovDelay[ax][ay]--;
9002     if (MovDelay[ax][ay])
9003       return;
9004   }
9005
9006   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9007   {
9008     int xx = ax+x1, yy = ay+y1;
9009     int old_element = Feld[xx][yy];
9010     int num_neighbours = 0;
9011
9012     if (!IN_LEV_FIELD(xx, yy))
9013       continue;
9014
9015     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9016     {
9017       int x = xx+x2, y = yy+y2;
9018
9019       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9020         continue;
9021
9022       boolean is_player_cell = (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y));
9023       boolean is_neighbour = FALSE;
9024
9025       if (level.use_life_bugs)
9026         is_neighbour =
9027           (((Feld[x][y] == element || is_player_cell) && !Stop[x][y]) ||
9028            (IS_FREE(x, y)                             &&  Stop[x][y]));
9029       else
9030         is_neighbour =
9031           (Last[x][y] == element || is_player_cell);
9032
9033       if (is_neighbour)
9034         num_neighbours++;
9035     }
9036
9037     boolean is_free = FALSE;
9038
9039     if (level.use_life_bugs)
9040       is_free = (IS_FREE(xx, yy));
9041     else
9042       is_free = (IS_FREE(xx, yy) && Last[xx][yy] == EL_EMPTY);
9043
9044     if (xx == ax && yy == ay)           // field in the middle
9045     {
9046       if (num_neighbours < life_parameter[0] ||
9047           num_neighbours > life_parameter[1])
9048       {
9049         Feld[xx][yy] = EL_EMPTY;
9050         if (Feld[xx][yy] != old_element)
9051           TEST_DrawLevelField(xx, yy);
9052         Stop[xx][yy] = TRUE;
9053         changed = TRUE;
9054       }
9055     }
9056     else if (is_free || CAN_GROW_INTO(Feld[xx][yy]))
9057     {                                   // free border field
9058       if (num_neighbours >= life_parameter[2] &&
9059           num_neighbours <= life_parameter[3])
9060       {
9061         Feld[xx][yy] = element;
9062         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9063         if (Feld[xx][yy] != old_element)
9064           TEST_DrawLevelField(xx, yy);
9065         Stop[xx][yy] = TRUE;
9066         changed = TRUE;
9067       }
9068     }
9069   }
9070
9071   if (changed)
9072     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9073                    SND_GAME_OF_LIFE_GROWING);
9074 }
9075
9076 static void InitRobotWheel(int x, int y)
9077 {
9078   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9079 }
9080
9081 static void RunRobotWheel(int x, int y)
9082 {
9083   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9084 }
9085
9086 static void StopRobotWheel(int x, int y)
9087 {
9088   if (game.robot_wheel_x == x &&
9089       game.robot_wheel_y == y)
9090   {
9091     game.robot_wheel_x = -1;
9092     game.robot_wheel_y = -1;
9093     game.robot_wheel_active = FALSE;
9094   }
9095 }
9096
9097 static void InitTimegateWheel(int x, int y)
9098 {
9099   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9100 }
9101
9102 static void RunTimegateWheel(int x, int y)
9103 {
9104   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9105 }
9106
9107 static void InitMagicBallDelay(int x, int y)
9108 {
9109   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9110 }
9111
9112 static void ActivateMagicBall(int bx, int by)
9113 {
9114   int x, y;
9115
9116   if (level.ball_random)
9117   {
9118     int pos_border = RND(8);    // select one of the eight border elements
9119     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9120     int xx = pos_content % 3;
9121     int yy = pos_content / 3;
9122
9123     x = bx - 1 + xx;
9124     y = by - 1 + yy;
9125
9126     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9127       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9128   }
9129   else
9130   {
9131     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9132     {
9133       int xx = x - bx + 1;
9134       int yy = y - by + 1;
9135
9136       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9137         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9138     }
9139   }
9140
9141   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9142 }
9143
9144 static void CheckExit(int x, int y)
9145 {
9146   if (game.gems_still_needed > 0 ||
9147       game.sokoban_fields_still_needed > 0 ||
9148       game.sokoban_objects_still_needed > 0 ||
9149       game.lights_still_needed > 0)
9150   {
9151     int element = Feld[x][y];
9152     int graphic = el2img(element);
9153
9154     if (IS_ANIMATED(graphic))
9155       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9156
9157     return;
9158   }
9159
9160   // do not re-open exit door closed after last player
9161   if (game.all_players_gone)
9162     return;
9163
9164   Feld[x][y] = EL_EXIT_OPENING;
9165
9166   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9167 }
9168
9169 static void CheckExitEM(int x, int y)
9170 {
9171   if (game.gems_still_needed > 0 ||
9172       game.sokoban_fields_still_needed > 0 ||
9173       game.sokoban_objects_still_needed > 0 ||
9174       game.lights_still_needed > 0)
9175   {
9176     int element = Feld[x][y];
9177     int graphic = el2img(element);
9178
9179     if (IS_ANIMATED(graphic))
9180       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9181
9182     return;
9183   }
9184
9185   // do not re-open exit door closed after last player
9186   if (game.all_players_gone)
9187     return;
9188
9189   Feld[x][y] = EL_EM_EXIT_OPENING;
9190
9191   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9192 }
9193
9194 static void CheckExitSteel(int x, int y)
9195 {
9196   if (game.gems_still_needed > 0 ||
9197       game.sokoban_fields_still_needed > 0 ||
9198       game.sokoban_objects_still_needed > 0 ||
9199       game.lights_still_needed > 0)
9200   {
9201     int element = Feld[x][y];
9202     int graphic = el2img(element);
9203
9204     if (IS_ANIMATED(graphic))
9205       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9206
9207     return;
9208   }
9209
9210   // do not re-open exit door closed after last player
9211   if (game.all_players_gone)
9212     return;
9213
9214   Feld[x][y] = EL_STEEL_EXIT_OPENING;
9215
9216   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9217 }
9218
9219 static void CheckExitSteelEM(int x, int y)
9220 {
9221   if (game.gems_still_needed > 0 ||
9222       game.sokoban_fields_still_needed > 0 ||
9223       game.sokoban_objects_still_needed > 0 ||
9224       game.lights_still_needed > 0)
9225   {
9226     int element = Feld[x][y];
9227     int graphic = el2img(element);
9228
9229     if (IS_ANIMATED(graphic))
9230       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9231
9232     return;
9233   }
9234
9235   // do not re-open exit door closed after last player
9236   if (game.all_players_gone)
9237     return;
9238
9239   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9240
9241   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9242 }
9243
9244 static void CheckExitSP(int x, int y)
9245 {
9246   if (game.gems_still_needed > 0)
9247   {
9248     int element = Feld[x][y];
9249     int graphic = el2img(element);
9250
9251     if (IS_ANIMATED(graphic))
9252       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9253
9254     return;
9255   }
9256
9257   // do not re-open exit door closed after last player
9258   if (game.all_players_gone)
9259     return;
9260
9261   Feld[x][y] = EL_SP_EXIT_OPENING;
9262
9263   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9264 }
9265
9266 static void CloseAllOpenTimegates(void)
9267 {
9268   int x, y;
9269
9270   SCAN_PLAYFIELD(x, y)
9271   {
9272     int element = Feld[x][y];
9273
9274     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9275     {
9276       Feld[x][y] = EL_TIMEGATE_CLOSING;
9277
9278       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9279     }
9280   }
9281 }
9282
9283 static void DrawTwinkleOnField(int x, int y)
9284 {
9285   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9286     return;
9287
9288   if (Feld[x][y] == EL_BD_DIAMOND)
9289     return;
9290
9291   if (MovDelay[x][y] == 0)      // next animation frame
9292     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9293
9294   if (MovDelay[x][y] != 0)      // wait some time before next frame
9295   {
9296     MovDelay[x][y]--;
9297
9298     DrawLevelElementAnimation(x, y, Feld[x][y]);
9299
9300     if (MovDelay[x][y] != 0)
9301     {
9302       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9303                                            10 - MovDelay[x][y]);
9304
9305       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9306     }
9307   }
9308 }
9309
9310 static void MauerWaechst(int x, int y)
9311 {
9312   int delay = 6;
9313
9314   if (!MovDelay[x][y])          // next animation frame
9315     MovDelay[x][y] = 3 * delay;
9316
9317   if (MovDelay[x][y])           // wait some time before next frame
9318   {
9319     MovDelay[x][y]--;
9320
9321     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9322     {
9323       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9324       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9325
9326       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9327     }
9328
9329     if (!MovDelay[x][y])
9330     {
9331       if (MovDir[x][y] == MV_LEFT)
9332       {
9333         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9334           TEST_DrawLevelField(x - 1, y);
9335       }
9336       else if (MovDir[x][y] == MV_RIGHT)
9337       {
9338         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9339           TEST_DrawLevelField(x + 1, y);
9340       }
9341       else if (MovDir[x][y] == MV_UP)
9342       {
9343         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9344           TEST_DrawLevelField(x, y - 1);
9345       }
9346       else
9347       {
9348         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9349           TEST_DrawLevelField(x, y + 1);
9350       }
9351
9352       Feld[x][y] = Store[x][y];
9353       Store[x][y] = 0;
9354       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9355       TEST_DrawLevelField(x, y);
9356     }
9357   }
9358 }
9359
9360 static void MauerAbleger(int ax, int ay)
9361 {
9362   int element = Feld[ax][ay];
9363   int graphic = el2img(element);
9364   boolean oben_frei = FALSE, unten_frei = FALSE;
9365   boolean links_frei = FALSE, rechts_frei = FALSE;
9366   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9367   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9368   boolean new_wall = FALSE;
9369
9370   if (IS_ANIMATED(graphic))
9371     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9372
9373   if (!MovDelay[ax][ay])        // start building new wall
9374     MovDelay[ax][ay] = 6;
9375
9376   if (MovDelay[ax][ay])         // wait some time before building new wall
9377   {
9378     MovDelay[ax][ay]--;
9379     if (MovDelay[ax][ay])
9380       return;
9381   }
9382
9383   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9384     oben_frei = TRUE;
9385   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9386     unten_frei = TRUE;
9387   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9388     links_frei = TRUE;
9389   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9390     rechts_frei = TRUE;
9391
9392   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9393       element == EL_EXPANDABLE_WALL_ANY)
9394   {
9395     if (oben_frei)
9396     {
9397       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9398       Store[ax][ay-1] = element;
9399       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9400       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9401         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9402                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9403       new_wall = TRUE;
9404     }
9405     if (unten_frei)
9406     {
9407       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9408       Store[ax][ay+1] = element;
9409       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9410       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9411         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9412                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9413       new_wall = TRUE;
9414     }
9415   }
9416
9417   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9418       element == EL_EXPANDABLE_WALL_ANY ||
9419       element == EL_EXPANDABLE_WALL ||
9420       element == EL_BD_EXPANDABLE_WALL)
9421   {
9422     if (links_frei)
9423     {
9424       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9425       Store[ax-1][ay] = element;
9426       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9427       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9428         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9429                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9430       new_wall = TRUE;
9431     }
9432
9433     if (rechts_frei)
9434     {
9435       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9436       Store[ax+1][ay] = element;
9437       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9438       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9439         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9440                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9441       new_wall = TRUE;
9442     }
9443   }
9444
9445   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9446     TEST_DrawLevelField(ax, ay);
9447
9448   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9449     oben_massiv = TRUE;
9450   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9451     unten_massiv = TRUE;
9452   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9453     links_massiv = TRUE;
9454   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9455     rechts_massiv = TRUE;
9456
9457   if (((oben_massiv && unten_massiv) ||
9458        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9459        element == EL_EXPANDABLE_WALL) &&
9460       ((links_massiv && rechts_massiv) ||
9461        element == EL_EXPANDABLE_WALL_VERTICAL))
9462     Feld[ax][ay] = EL_WALL;
9463
9464   if (new_wall)
9465     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9466 }
9467
9468 static void MauerAblegerStahl(int ax, int ay)
9469 {
9470   int element = Feld[ax][ay];
9471   int graphic = el2img(element);
9472   boolean oben_frei = FALSE, unten_frei = FALSE;
9473   boolean links_frei = FALSE, rechts_frei = FALSE;
9474   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9475   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9476   boolean new_wall = FALSE;
9477
9478   if (IS_ANIMATED(graphic))
9479     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9480
9481   if (!MovDelay[ax][ay])        // start building new wall
9482     MovDelay[ax][ay] = 6;
9483
9484   if (MovDelay[ax][ay])         // wait some time before building new wall
9485   {
9486     MovDelay[ax][ay]--;
9487     if (MovDelay[ax][ay])
9488       return;
9489   }
9490
9491   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9492     oben_frei = TRUE;
9493   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9494     unten_frei = TRUE;
9495   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9496     links_frei = TRUE;
9497   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9498     rechts_frei = TRUE;
9499
9500   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9501       element == EL_EXPANDABLE_STEELWALL_ANY)
9502   {
9503     if (oben_frei)
9504     {
9505       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9506       Store[ax][ay-1] = element;
9507       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9508       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9509         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9510                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9511       new_wall = TRUE;
9512     }
9513     if (unten_frei)
9514     {
9515       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9516       Store[ax][ay+1] = element;
9517       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9518       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9519         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9520                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9521       new_wall = TRUE;
9522     }
9523   }
9524
9525   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9526       element == EL_EXPANDABLE_STEELWALL_ANY)
9527   {
9528     if (links_frei)
9529     {
9530       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9531       Store[ax-1][ay] = element;
9532       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9533       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9534         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9535                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9536       new_wall = TRUE;
9537     }
9538
9539     if (rechts_frei)
9540     {
9541       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9542       Store[ax+1][ay] = element;
9543       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9544       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9545         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9546                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9547       new_wall = TRUE;
9548     }
9549   }
9550
9551   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9552     oben_massiv = TRUE;
9553   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9554     unten_massiv = TRUE;
9555   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9556     links_massiv = TRUE;
9557   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9558     rechts_massiv = TRUE;
9559
9560   if (((oben_massiv && unten_massiv) ||
9561        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9562       ((links_massiv && rechts_massiv) ||
9563        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9564     Feld[ax][ay] = EL_STEELWALL;
9565
9566   if (new_wall)
9567     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9568 }
9569
9570 static void CheckForDragon(int x, int y)
9571 {
9572   int i, j;
9573   boolean dragon_found = FALSE;
9574   static int xy[4][2] =
9575   {
9576     { 0, -1 },
9577     { -1, 0 },
9578     { +1, 0 },
9579     { 0, +1 }
9580   };
9581
9582   for (i = 0; i < NUM_DIRECTIONS; i++)
9583   {
9584     for (j = 0; j < 4; j++)
9585     {
9586       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9587
9588       if (IN_LEV_FIELD(xx, yy) &&
9589           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9590       {
9591         if (Feld[xx][yy] == EL_DRAGON)
9592           dragon_found = TRUE;
9593       }
9594       else
9595         break;
9596     }
9597   }
9598
9599   if (!dragon_found)
9600   {
9601     for (i = 0; i < NUM_DIRECTIONS; i++)
9602     {
9603       for (j = 0; j < 3; j++)
9604       {
9605         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9606   
9607         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9608         {
9609           Feld[xx][yy] = EL_EMPTY;
9610           TEST_DrawLevelField(xx, yy);
9611         }
9612         else
9613           break;
9614       }
9615     }
9616   }
9617 }
9618
9619 static void InitBuggyBase(int x, int y)
9620 {
9621   int element = Feld[x][y];
9622   int activating_delay = FRAMES_PER_SECOND / 4;
9623
9624   ChangeDelay[x][y] =
9625     (element == EL_SP_BUGGY_BASE ?
9626      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9627      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9628      activating_delay :
9629      element == EL_SP_BUGGY_BASE_ACTIVE ?
9630      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9631 }
9632
9633 static void WarnBuggyBase(int x, int y)
9634 {
9635   int i;
9636   static int xy[4][2] =
9637   {
9638     { 0, -1 },
9639     { -1, 0 },
9640     { +1, 0 },
9641     { 0, +1 }
9642   };
9643
9644   for (i = 0; i < NUM_DIRECTIONS; i++)
9645   {
9646     int xx = x + xy[i][0];
9647     int yy = y + xy[i][1];
9648
9649     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9650     {
9651       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9652
9653       break;
9654     }
9655   }
9656 }
9657
9658 static void InitTrap(int x, int y)
9659 {
9660   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9661 }
9662
9663 static void ActivateTrap(int x, int y)
9664 {
9665   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9666 }
9667
9668 static void ChangeActiveTrap(int x, int y)
9669 {
9670   int graphic = IMG_TRAP_ACTIVE;
9671
9672   // if new animation frame was drawn, correct crumbled sand border
9673   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9674     TEST_DrawLevelFieldCrumbled(x, y);
9675 }
9676
9677 static int getSpecialActionElement(int element, int number, int base_element)
9678 {
9679   return (element != EL_EMPTY ? element :
9680           number != -1 ? base_element + number - 1 :
9681           EL_EMPTY);
9682 }
9683
9684 static int getModifiedActionNumber(int value_old, int operator, int operand,
9685                                    int value_min, int value_max)
9686 {
9687   int value_new = (operator == CA_MODE_SET      ? operand :
9688                    operator == CA_MODE_ADD      ? value_old + operand :
9689                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9690                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9691                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9692                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9693                    value_old);
9694
9695   return (value_new < value_min ? value_min :
9696           value_new > value_max ? value_max :
9697           value_new);
9698 }
9699
9700 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9701 {
9702   struct ElementInfo *ei = &element_info[element];
9703   struct ElementChangeInfo *change = &ei->change_page[page];
9704   int target_element = change->target_element;
9705   int action_type = change->action_type;
9706   int action_mode = change->action_mode;
9707   int action_arg = change->action_arg;
9708   int action_element = change->action_element;
9709   int i;
9710
9711   if (!change->has_action)
9712     return;
9713
9714   // ---------- determine action paramater values -----------------------------
9715
9716   int level_time_value =
9717     (level.time > 0 ? TimeLeft :
9718      TimePlayed);
9719
9720   int action_arg_element_raw =
9721     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9722      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9723      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9724      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
9725      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9726      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
9727      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
9728      EL_EMPTY);
9729   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9730
9731   int action_arg_direction =
9732     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9733      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9734      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9735      change->actual_trigger_side :
9736      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9737      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9738      MV_NONE);
9739
9740   int action_arg_number_min =
9741     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9742      CA_ARG_MIN);
9743
9744   int action_arg_number_max =
9745     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9746      action_type == CA_SET_LEVEL_GEMS ? 999 :
9747      action_type == CA_SET_LEVEL_TIME ? 9999 :
9748      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9749      action_type == CA_SET_CE_VALUE ? 9999 :
9750      action_type == CA_SET_CE_SCORE ? 9999 :
9751      CA_ARG_MAX);
9752
9753   int action_arg_number_reset =
9754     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9755      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9756      action_type == CA_SET_LEVEL_TIME ? level.time :
9757      action_type == CA_SET_LEVEL_SCORE ? 0 :
9758      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9759      action_type == CA_SET_CE_SCORE ? 0 :
9760      0);
9761
9762   int action_arg_number =
9763     (action_arg <= CA_ARG_MAX ? action_arg :
9764      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9765      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9766      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9767      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9768      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9769      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9770      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9771      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9772      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9773      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9774      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? game.gems_still_needed :
9775      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? game.score :
9776      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9777      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9778      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9779      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9780      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9781      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9782      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
9783      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9784      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
9785      -1);
9786
9787   int action_arg_number_old =
9788     (action_type == CA_SET_LEVEL_GEMS ? game.gems_still_needed :
9789      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9790      action_type == CA_SET_LEVEL_SCORE ? game.score :
9791      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9792      action_type == CA_SET_CE_SCORE ? ei->collect_score :
9793      0);
9794
9795   int action_arg_number_new =
9796     getModifiedActionNumber(action_arg_number_old,
9797                             action_mode, action_arg_number,
9798                             action_arg_number_min, action_arg_number_max);
9799
9800   int trigger_player_bits =
9801     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9802      change->actual_trigger_player_bits : change->trigger_player);
9803
9804   int action_arg_player_bits =
9805     (action_arg >= CA_ARG_PLAYER_1 &&
9806      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9807      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9808      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9809      PLAYER_BITS_ANY);
9810
9811   // ---------- execute action  -----------------------------------------------
9812
9813   switch (action_type)
9814   {
9815     case CA_NO_ACTION:
9816     {
9817       return;
9818     }
9819
9820     // ---------- level actions  ----------------------------------------------
9821
9822     case CA_RESTART_LEVEL:
9823     {
9824       game.restart_level = TRUE;
9825
9826       break;
9827     }
9828
9829     case CA_SHOW_ENVELOPE:
9830     {
9831       int element = getSpecialActionElement(action_arg_element,
9832                                             action_arg_number, EL_ENVELOPE_1);
9833
9834       if (IS_ENVELOPE(element))
9835         local_player->show_envelope = element;
9836
9837       break;
9838     }
9839
9840     case CA_SET_LEVEL_TIME:
9841     {
9842       if (level.time > 0)       // only modify limited time value
9843       {
9844         TimeLeft = action_arg_number_new;
9845
9846         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9847
9848         DisplayGameControlValues();
9849
9850         if (!TimeLeft && setup.time_limit)
9851           for (i = 0; i < MAX_PLAYERS; i++)
9852             KillPlayer(&stored_player[i]);
9853       }
9854
9855       break;
9856     }
9857
9858     case CA_SET_LEVEL_SCORE:
9859     {
9860       game.score = action_arg_number_new;
9861
9862       game_panel_controls[GAME_PANEL_SCORE].value = game.score;
9863
9864       DisplayGameControlValues();
9865
9866       break;
9867     }
9868
9869     case CA_SET_LEVEL_GEMS:
9870     {
9871       game.gems_still_needed = action_arg_number_new;
9872
9873       game.snapshot.collected_item = TRUE;
9874
9875       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
9876
9877       DisplayGameControlValues();
9878
9879       break;
9880     }
9881
9882     case CA_SET_LEVEL_WIND:
9883     {
9884       game.wind_direction = action_arg_direction;
9885
9886       break;
9887     }
9888
9889     case CA_SET_LEVEL_RANDOM_SEED:
9890     {
9891       // ensure that setting a new random seed while playing is predictable
9892       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
9893
9894       break;
9895     }
9896
9897     // ---------- player actions  ---------------------------------------------
9898
9899     case CA_MOVE_PLAYER:
9900     case CA_MOVE_PLAYER_NEW:
9901     {
9902       // automatically move to the next field in specified direction
9903       for (i = 0; i < MAX_PLAYERS; i++)
9904         if (trigger_player_bits & (1 << i))
9905           if (action_type == CA_MOVE_PLAYER ||
9906               stored_player[i].MovPos == 0)
9907             stored_player[i].programmed_action = action_arg_direction;
9908
9909       break;
9910     }
9911
9912     case CA_EXIT_PLAYER:
9913     {
9914       for (i = 0; i < MAX_PLAYERS; i++)
9915         if (action_arg_player_bits & (1 << i))
9916           ExitPlayer(&stored_player[i]);
9917
9918       if (game.players_still_needed == 0)
9919         LevelSolved();
9920
9921       break;
9922     }
9923
9924     case CA_KILL_PLAYER:
9925     {
9926       for (i = 0; i < MAX_PLAYERS; i++)
9927         if (action_arg_player_bits & (1 << i))
9928           KillPlayer(&stored_player[i]);
9929
9930       break;
9931     }
9932
9933     case CA_SET_PLAYER_KEYS:
9934     {
9935       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9936       int element = getSpecialActionElement(action_arg_element,
9937                                             action_arg_number, EL_KEY_1);
9938
9939       if (IS_KEY(element))
9940       {
9941         for (i = 0; i < MAX_PLAYERS; i++)
9942         {
9943           if (trigger_player_bits & (1 << i))
9944           {
9945             stored_player[i].key[KEY_NR(element)] = key_state;
9946
9947             DrawGameDoorValues();
9948           }
9949         }
9950       }
9951
9952       break;
9953     }
9954
9955     case CA_SET_PLAYER_SPEED:
9956     {
9957       for (i = 0; i < MAX_PLAYERS; i++)
9958       {
9959         if (trigger_player_bits & (1 << i))
9960         {
9961           int move_stepsize = TILEX / stored_player[i].move_delay_value;
9962
9963           if (action_arg == CA_ARG_SPEED_FASTER &&
9964               stored_player[i].cannot_move)
9965           {
9966             action_arg_number = STEPSIZE_VERY_SLOW;
9967           }
9968           else if (action_arg == CA_ARG_SPEED_SLOWER ||
9969                    action_arg == CA_ARG_SPEED_FASTER)
9970           {
9971             action_arg_number = 2;
9972             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9973                            CA_MODE_MULTIPLY);
9974           }
9975           else if (action_arg == CA_ARG_NUMBER_RESET)
9976           {
9977             action_arg_number = level.initial_player_stepsize[i];
9978           }
9979
9980           move_stepsize =
9981             getModifiedActionNumber(move_stepsize,
9982                                     action_mode,
9983                                     action_arg_number,
9984                                     action_arg_number_min,
9985                                     action_arg_number_max);
9986
9987           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9988         }
9989       }
9990
9991       break;
9992     }
9993
9994     case CA_SET_PLAYER_SHIELD:
9995     {
9996       for (i = 0; i < MAX_PLAYERS; i++)
9997       {
9998         if (trigger_player_bits & (1 << i))
9999         {
10000           if (action_arg == CA_ARG_SHIELD_OFF)
10001           {
10002             stored_player[i].shield_normal_time_left = 0;
10003             stored_player[i].shield_deadly_time_left = 0;
10004           }
10005           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10006           {
10007             stored_player[i].shield_normal_time_left = 999999;
10008           }
10009           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10010           {
10011             stored_player[i].shield_normal_time_left = 999999;
10012             stored_player[i].shield_deadly_time_left = 999999;
10013           }
10014         }
10015       }
10016
10017       break;
10018     }
10019
10020     case CA_SET_PLAYER_GRAVITY:
10021     {
10022       for (i = 0; i < MAX_PLAYERS; i++)
10023       {
10024         if (trigger_player_bits & (1 << i))
10025         {
10026           stored_player[i].gravity =
10027             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10028              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10029              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10030              stored_player[i].gravity);
10031         }
10032       }
10033
10034       break;
10035     }
10036
10037     case CA_SET_PLAYER_ARTWORK:
10038     {
10039       for (i = 0; i < MAX_PLAYERS; i++)
10040       {
10041         if (trigger_player_bits & (1 << i))
10042         {
10043           int artwork_element = action_arg_element;
10044
10045           if (action_arg == CA_ARG_ELEMENT_RESET)
10046             artwork_element =
10047               (level.use_artwork_element[i] ? level.artwork_element[i] :
10048                stored_player[i].element_nr);
10049
10050           if (stored_player[i].artwork_element != artwork_element)
10051             stored_player[i].Frame = 0;
10052
10053           stored_player[i].artwork_element = artwork_element;
10054
10055           SetPlayerWaiting(&stored_player[i], FALSE);
10056
10057           // set number of special actions for bored and sleeping animation
10058           stored_player[i].num_special_action_bored =
10059             get_num_special_action(artwork_element,
10060                                    ACTION_BORING_1, ACTION_BORING_LAST);
10061           stored_player[i].num_special_action_sleeping =
10062             get_num_special_action(artwork_element,
10063                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10064         }
10065       }
10066
10067       break;
10068     }
10069
10070     case CA_SET_PLAYER_INVENTORY:
10071     {
10072       for (i = 0; i < MAX_PLAYERS; i++)
10073       {
10074         struct PlayerInfo *player = &stored_player[i];
10075         int j, k;
10076
10077         if (trigger_player_bits & (1 << i))
10078         {
10079           int inventory_element = action_arg_element;
10080
10081           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10082               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10083               action_arg == CA_ARG_ELEMENT_ACTION)
10084           {
10085             int element = inventory_element;
10086             int collect_count = element_info[element].collect_count_initial;
10087
10088             if (!IS_CUSTOM_ELEMENT(element))
10089               collect_count = 1;
10090
10091             if (collect_count == 0)
10092               player->inventory_infinite_element = element;
10093             else
10094               for (k = 0; k < collect_count; k++)
10095                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10096                   player->inventory_element[player->inventory_size++] =
10097                     element;
10098           }
10099           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10100                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10101                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10102           {
10103             if (player->inventory_infinite_element != EL_UNDEFINED &&
10104                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10105                                      action_arg_element_raw))
10106               player->inventory_infinite_element = EL_UNDEFINED;
10107
10108             for (k = 0, j = 0; j < player->inventory_size; j++)
10109             {
10110               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10111                                         action_arg_element_raw))
10112                 player->inventory_element[k++] = player->inventory_element[j];
10113             }
10114
10115             player->inventory_size = k;
10116           }
10117           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10118           {
10119             if (player->inventory_size > 0)
10120             {
10121               for (j = 0; j < player->inventory_size - 1; j++)
10122                 player->inventory_element[j] = player->inventory_element[j + 1];
10123
10124               player->inventory_size--;
10125             }
10126           }
10127           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10128           {
10129             if (player->inventory_size > 0)
10130               player->inventory_size--;
10131           }
10132           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10133           {
10134             player->inventory_infinite_element = EL_UNDEFINED;
10135             player->inventory_size = 0;
10136           }
10137           else if (action_arg == CA_ARG_INVENTORY_RESET)
10138           {
10139             player->inventory_infinite_element = EL_UNDEFINED;
10140             player->inventory_size = 0;
10141
10142             if (level.use_initial_inventory[i])
10143             {
10144               for (j = 0; j < level.initial_inventory_size[i]; j++)
10145               {
10146                 int element = level.initial_inventory_content[i][j];
10147                 int collect_count = element_info[element].collect_count_initial;
10148
10149                 if (!IS_CUSTOM_ELEMENT(element))
10150                   collect_count = 1;
10151
10152                 if (collect_count == 0)
10153                   player->inventory_infinite_element = element;
10154                 else
10155                   for (k = 0; k < collect_count; k++)
10156                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10157                       player->inventory_element[player->inventory_size++] =
10158                         element;
10159               }
10160             }
10161           }
10162         }
10163       }
10164
10165       break;
10166     }
10167
10168     // ---------- CE actions  -------------------------------------------------
10169
10170     case CA_SET_CE_VALUE:
10171     {
10172       int last_ce_value = CustomValue[x][y];
10173
10174       CustomValue[x][y] = action_arg_number_new;
10175
10176       if (CustomValue[x][y] != last_ce_value)
10177       {
10178         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10179         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10180
10181         if (CustomValue[x][y] == 0)
10182         {
10183           // reset change counter (else CE_VALUE_GETS_ZERO would not work)
10184           ChangeCount[x][y] = 0;        // allow at least one more change
10185
10186           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10187           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10188         }
10189       }
10190
10191       break;
10192     }
10193
10194     case CA_SET_CE_SCORE:
10195     {
10196       int last_ce_score = ei->collect_score;
10197
10198       ei->collect_score = action_arg_number_new;
10199
10200       if (ei->collect_score != last_ce_score)
10201       {
10202         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10203         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10204
10205         if (ei->collect_score == 0)
10206         {
10207           int xx, yy;
10208
10209           // reset change counter (else CE_SCORE_GETS_ZERO would not work)
10210           ChangeCount[x][y] = 0;        // allow at least one more change
10211
10212           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10213           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10214
10215           /*
10216             This is a very special case that seems to be a mixture between
10217             CheckElementChange() and CheckTriggeredElementChange(): while
10218             the first one only affects single elements that are triggered
10219             directly, the second one affects multiple elements in the playfield
10220             that are triggered indirectly by another element. This is a third
10221             case: Changing the CE score always affects multiple identical CEs,
10222             so every affected CE must be checked, not only the single CE for
10223             which the CE score was changed in the first place (as every instance
10224             of that CE shares the same CE score, and therefore also can change)!
10225           */
10226           SCAN_PLAYFIELD(xx, yy)
10227           {
10228             if (Feld[xx][yy] == element)
10229               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10230                                  CE_SCORE_GETS_ZERO);
10231           }
10232         }
10233       }
10234
10235       break;
10236     }
10237
10238     case CA_SET_CE_ARTWORK:
10239     {
10240       int artwork_element = action_arg_element;
10241       boolean reset_frame = FALSE;
10242       int xx, yy;
10243
10244       if (action_arg == CA_ARG_ELEMENT_RESET)
10245         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10246                            element);
10247
10248       if (ei->gfx_element != artwork_element)
10249         reset_frame = TRUE;
10250
10251       ei->gfx_element = artwork_element;
10252
10253       SCAN_PLAYFIELD(xx, yy)
10254       {
10255         if (Feld[xx][yy] == element)
10256         {
10257           if (reset_frame)
10258           {
10259             ResetGfxAnimation(xx, yy);
10260             ResetRandomAnimationValue(xx, yy);
10261           }
10262
10263           TEST_DrawLevelField(xx, yy);
10264         }
10265       }
10266
10267       break;
10268     }
10269
10270     // ---------- engine actions  ---------------------------------------------
10271
10272     case CA_SET_ENGINE_SCAN_MODE:
10273     {
10274       InitPlayfieldScanMode(action_arg);
10275
10276       break;
10277     }
10278
10279     default:
10280       break;
10281   }
10282 }
10283
10284 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10285 {
10286   int old_element = Feld[x][y];
10287   int new_element = GetElementFromGroupElement(element);
10288   int previous_move_direction = MovDir[x][y];
10289   int last_ce_value = CustomValue[x][y];
10290   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10291   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10292   boolean add_player_onto_element = (new_element_is_player &&
10293                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10294                                      IS_WALKABLE(old_element));
10295
10296   if (!add_player_onto_element)
10297   {
10298     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10299       RemoveMovingField(x, y);
10300     else
10301       RemoveField(x, y);
10302
10303     Feld[x][y] = new_element;
10304
10305     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10306       MovDir[x][y] = previous_move_direction;
10307
10308     if (element_info[new_element].use_last_ce_value)
10309       CustomValue[x][y] = last_ce_value;
10310
10311     InitField_WithBug1(x, y, FALSE);
10312
10313     new_element = Feld[x][y];   // element may have changed
10314
10315     ResetGfxAnimation(x, y);
10316     ResetRandomAnimationValue(x, y);
10317
10318     TEST_DrawLevelField(x, y);
10319
10320     if (GFX_CRUMBLED(new_element))
10321       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10322   }
10323
10324   // check if element under the player changes from accessible to unaccessible
10325   // (needed for special case of dropping element which then changes)
10326   // (must be checked after creating new element for walkable group elements)
10327   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10328       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10329   {
10330     Bang(x, y);
10331
10332     return;
10333   }
10334
10335   // "ChangeCount" not set yet to allow "entered by player" change one time
10336   if (new_element_is_player)
10337     RelocatePlayer(x, y, new_element);
10338
10339   if (is_change)
10340     ChangeCount[x][y]++;        // count number of changes in the same frame
10341
10342   TestIfBadThingTouchesPlayer(x, y);
10343   TestIfPlayerTouchesCustomElement(x, y);
10344   TestIfElementTouchesCustomElement(x, y);
10345 }
10346
10347 static void CreateField(int x, int y, int element)
10348 {
10349   CreateFieldExt(x, y, element, FALSE);
10350 }
10351
10352 static void CreateElementFromChange(int x, int y, int element)
10353 {
10354   element = GET_VALID_RUNTIME_ELEMENT(element);
10355
10356   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10357   {
10358     int old_element = Feld[x][y];
10359
10360     // prevent changed element from moving in same engine frame
10361     // unless both old and new element can either fall or move
10362     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10363         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10364       Stop[x][y] = TRUE;
10365   }
10366
10367   CreateFieldExt(x, y, element, TRUE);
10368 }
10369
10370 static boolean ChangeElement(int x, int y, int element, int page)
10371 {
10372   struct ElementInfo *ei = &element_info[element];
10373   struct ElementChangeInfo *change = &ei->change_page[page];
10374   int ce_value = CustomValue[x][y];
10375   int ce_score = ei->collect_score;
10376   int target_element;
10377   int old_element = Feld[x][y];
10378
10379   // always use default change event to prevent running into a loop
10380   if (ChangeEvent[x][y] == -1)
10381     ChangeEvent[x][y] = CE_DELAY;
10382
10383   if (ChangeEvent[x][y] == CE_DELAY)
10384   {
10385     // reset actual trigger element, trigger player and action element
10386     change->actual_trigger_element = EL_EMPTY;
10387     change->actual_trigger_player = EL_EMPTY;
10388     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10389     change->actual_trigger_side = CH_SIDE_NONE;
10390     change->actual_trigger_ce_value = 0;
10391     change->actual_trigger_ce_score = 0;
10392   }
10393
10394   // do not change elements more than a specified maximum number of changes
10395   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10396     return FALSE;
10397
10398   ChangeCount[x][y]++;          // count number of changes in the same frame
10399
10400   if (change->explode)
10401   {
10402     Bang(x, y);
10403
10404     return TRUE;
10405   }
10406
10407   if (change->use_target_content)
10408   {
10409     boolean complete_replace = TRUE;
10410     boolean can_replace[3][3];
10411     int xx, yy;
10412
10413     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10414     {
10415       boolean is_empty;
10416       boolean is_walkable;
10417       boolean is_diggable;
10418       boolean is_collectible;
10419       boolean is_removable;
10420       boolean is_destructible;
10421       int ex = x + xx - 1;
10422       int ey = y + yy - 1;
10423       int content_element = change->target_content.e[xx][yy];
10424       int e;
10425
10426       can_replace[xx][yy] = TRUE;
10427
10428       if (ex == x && ey == y)   // do not check changing element itself
10429         continue;
10430
10431       if (content_element == EL_EMPTY_SPACE)
10432       {
10433         can_replace[xx][yy] = FALSE;    // do not replace border with space
10434
10435         continue;
10436       }
10437
10438       if (!IN_LEV_FIELD(ex, ey))
10439       {
10440         can_replace[xx][yy] = FALSE;
10441         complete_replace = FALSE;
10442
10443         continue;
10444       }
10445
10446       e = Feld[ex][ey];
10447
10448       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10449         e = MovingOrBlocked2Element(ex, ey);
10450
10451       is_empty = (IS_FREE(ex, ey) ||
10452                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10453
10454       is_walkable     = (is_empty || IS_WALKABLE(e));
10455       is_diggable     = (is_empty || IS_DIGGABLE(e));
10456       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10457       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10458       is_removable    = (is_diggable || is_collectible);
10459
10460       can_replace[xx][yy] =
10461         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10462           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10463           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10464           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10465           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10466           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10467          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10468
10469       if (!can_replace[xx][yy])
10470         complete_replace = FALSE;
10471     }
10472
10473     if (!change->only_if_complete || complete_replace)
10474     {
10475       boolean something_has_changed = FALSE;
10476
10477       if (change->only_if_complete && change->use_random_replace &&
10478           RND(100) < change->random_percentage)
10479         return FALSE;
10480
10481       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10482       {
10483         int ex = x + xx - 1;
10484         int ey = y + yy - 1;
10485         int content_element;
10486
10487         if (can_replace[xx][yy] && (!change->use_random_replace ||
10488                                     RND(100) < change->random_percentage))
10489         {
10490           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10491             RemoveMovingField(ex, ey);
10492
10493           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10494
10495           content_element = change->target_content.e[xx][yy];
10496           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10497                                               ce_value, ce_score);
10498
10499           CreateElementFromChange(ex, ey, target_element);
10500
10501           something_has_changed = TRUE;
10502
10503           // for symmetry reasons, freeze newly created border elements
10504           if (ex != x || ey != y)
10505             Stop[ex][ey] = TRUE;        // no more moving in this frame
10506         }
10507       }
10508
10509       if (something_has_changed)
10510       {
10511         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10512         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10513       }
10514     }
10515   }
10516   else
10517   {
10518     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10519                                         ce_value, ce_score);
10520
10521     if (element == EL_DIAGONAL_GROWING ||
10522         element == EL_DIAGONAL_SHRINKING)
10523     {
10524       target_element = Store[x][y];
10525
10526       Store[x][y] = EL_EMPTY;
10527     }
10528
10529     CreateElementFromChange(x, y, target_element);
10530
10531     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10532     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10533   }
10534
10535   // this uses direct change before indirect change
10536   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10537
10538   return TRUE;
10539 }
10540
10541 static void HandleElementChange(int x, int y, int page)
10542 {
10543   int element = MovingOrBlocked2Element(x, y);
10544   struct ElementInfo *ei = &element_info[element];
10545   struct ElementChangeInfo *change = &ei->change_page[page];
10546   boolean handle_action_before_change = FALSE;
10547
10548 #ifdef DEBUG
10549   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10550       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10551   {
10552     printf("\n\n");
10553     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10554            x, y, element, element_info[element].token_name);
10555     printf("HandleElementChange(): This should never happen!\n");
10556     printf("\n\n");
10557   }
10558 #endif
10559
10560   // this can happen with classic bombs on walkable, changing elements
10561   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10562   {
10563     return;
10564   }
10565
10566   if (ChangeDelay[x][y] == 0)           // initialize element change
10567   {
10568     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10569
10570     if (change->can_change)
10571     {
10572       // !!! not clear why graphic animation should be reset at all here !!!
10573       // !!! UPDATE: but is needed for correct Snake Bite tail animation !!!
10574       // !!! SOLUTION: do not reset if graphics engine set to 4 or above !!!
10575
10576       /*
10577         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10578
10579         When using an animation frame delay of 1 (this only happens with
10580         "sp_zonk.moving.left/right" in the classic graphics), the default
10581         (non-moving) animation shows wrong animation frames (while the
10582         moving animation, like "sp_zonk.moving.left/right", is correct,
10583         so this graphical bug never shows up with the classic graphics).
10584         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10585         be drawn instead of the correct frames 0,1,2,3. This is caused by
10586         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10587         an element change: First when the change delay ("ChangeDelay[][]")
10588         counter has reached zero after decrementing, then a second time in
10589         the next frame (after "GfxFrame[][]" was already incremented) when
10590         "ChangeDelay[][]" is reset to the initial delay value again.
10591
10592         This causes frame 0 to be drawn twice, while the last frame won't
10593         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10594
10595         As some animations may already be cleverly designed around this bug
10596         (at least the "Snake Bite" snake tail animation does this), it cannot
10597         simply be fixed here without breaking such existing animations.
10598         Unfortunately, it cannot easily be detected if a graphics set was
10599         designed "before" or "after" the bug was fixed. As a workaround,
10600         a new graphics set option "game.graphics_engine_version" was added
10601         to be able to specify the game's major release version for which the
10602         graphics set was designed, which can then be used to decide if the
10603         bugfix should be used (version 4 and above) or not (version 3 or
10604         below, or if no version was specified at all, as with old sets).
10605
10606         (The wrong/fixed animation frames can be tested with the test level set
10607         "test_gfxframe" and level "000", which contains a specially prepared
10608         custom element at level position (x/y) == (11/9) which uses the zonk
10609         animation mentioned above. Using "game.graphics_engine_version: 4"
10610         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10611         This can also be seen from the debug output for this test element.)
10612       */
10613
10614       // when a custom element is about to change (for example by change delay),
10615       // do not reset graphic animation when the custom element is moving
10616       if (game.graphics_engine_version < 4 &&
10617           !IS_MOVING(x, y))
10618       {
10619         ResetGfxAnimation(x, y);
10620         ResetRandomAnimationValue(x, y);
10621       }
10622
10623       if (change->pre_change_function)
10624         change->pre_change_function(x, y);
10625     }
10626   }
10627
10628   ChangeDelay[x][y]--;
10629
10630   if (ChangeDelay[x][y] != 0)           // continue element change
10631   {
10632     if (change->can_change)
10633     {
10634       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10635
10636       if (IS_ANIMATED(graphic))
10637         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10638
10639       if (change->change_function)
10640         change->change_function(x, y);
10641     }
10642   }
10643   else                                  // finish element change
10644   {
10645     if (ChangePage[x][y] != -1)         // remember page from delayed change
10646     {
10647       page = ChangePage[x][y];
10648       ChangePage[x][y] = -1;
10649
10650       change = &ei->change_page[page];
10651     }
10652
10653     if (IS_MOVING(x, y))                // never change a running system ;-)
10654     {
10655       ChangeDelay[x][y] = 1;            // try change after next move step
10656       ChangePage[x][y] = page;          // remember page to use for change
10657
10658       return;
10659     }
10660
10661     // special case: set new level random seed before changing element
10662     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10663       handle_action_before_change = TRUE;
10664
10665     if (change->has_action && handle_action_before_change)
10666       ExecuteCustomElementAction(x, y, element, page);
10667
10668     if (change->can_change)
10669     {
10670       if (ChangeElement(x, y, element, page))
10671       {
10672         if (change->post_change_function)
10673           change->post_change_function(x, y);
10674       }
10675     }
10676
10677     if (change->has_action && !handle_action_before_change)
10678       ExecuteCustomElementAction(x, y, element, page);
10679   }
10680 }
10681
10682 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10683                                               int trigger_element,
10684                                               int trigger_event,
10685                                               int trigger_player,
10686                                               int trigger_side,
10687                                               int trigger_page)
10688 {
10689   boolean change_done_any = FALSE;
10690   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10691   int i;
10692
10693   if (!(trigger_events[trigger_element][trigger_event]))
10694     return FALSE;
10695
10696   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10697
10698   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10699   {
10700     int element = EL_CUSTOM_START + i;
10701     boolean change_done = FALSE;
10702     int p;
10703
10704     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10705         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10706       continue;
10707
10708     for (p = 0; p < element_info[element].num_change_pages; p++)
10709     {
10710       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10711
10712       if (change->can_change_or_has_action &&
10713           change->has_event[trigger_event] &&
10714           change->trigger_side & trigger_side &&
10715           change->trigger_player & trigger_player &&
10716           change->trigger_page & trigger_page_bits &&
10717           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10718       {
10719         change->actual_trigger_element = trigger_element;
10720         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10721         change->actual_trigger_player_bits = trigger_player;
10722         change->actual_trigger_side = trigger_side;
10723         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10724         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10725
10726         if ((change->can_change && !change_done) || change->has_action)
10727         {
10728           int x, y;
10729
10730           SCAN_PLAYFIELD(x, y)
10731           {
10732             if (Feld[x][y] == element)
10733             {
10734               if (change->can_change && !change_done)
10735               {
10736                 // if element already changed in this frame, not only prevent
10737                 // another element change (checked in ChangeElement()), but
10738                 // also prevent additional element actions for this element
10739
10740                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10741                     !level.use_action_after_change_bug)
10742                   continue;
10743
10744                 ChangeDelay[x][y] = 1;
10745                 ChangeEvent[x][y] = trigger_event;
10746
10747                 HandleElementChange(x, y, p);
10748               }
10749               else if (change->has_action)
10750               {
10751                 // if element already changed in this frame, not only prevent
10752                 // another element change (checked in ChangeElement()), but
10753                 // also prevent additional element actions for this element
10754
10755                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10756                     !level.use_action_after_change_bug)
10757                   continue;
10758
10759                 ExecuteCustomElementAction(x, y, element, p);
10760                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10761               }
10762             }
10763           }
10764
10765           if (change->can_change)
10766           {
10767             change_done = TRUE;
10768             change_done_any = TRUE;
10769           }
10770         }
10771       }
10772     }
10773   }
10774
10775   RECURSION_LOOP_DETECTION_END();
10776
10777   return change_done_any;
10778 }
10779
10780 static boolean CheckElementChangeExt(int x, int y,
10781                                      int element,
10782                                      int trigger_element,
10783                                      int trigger_event,
10784                                      int trigger_player,
10785                                      int trigger_side)
10786 {
10787   boolean change_done = FALSE;
10788   int p;
10789
10790   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10791       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10792     return FALSE;
10793
10794   if (Feld[x][y] == EL_BLOCKED)
10795   {
10796     Blocked2Moving(x, y, &x, &y);
10797     element = Feld[x][y];
10798   }
10799
10800   // check if element has already changed or is about to change after moving
10801   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10802        Feld[x][y] != element) ||
10803
10804       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10805        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10806         ChangePage[x][y] != -1)))
10807     return FALSE;
10808
10809   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10810
10811   for (p = 0; p < element_info[element].num_change_pages; p++)
10812   {
10813     struct ElementChangeInfo *change = &element_info[element].change_page[p];
10814
10815     /* check trigger element for all events where the element that is checked
10816        for changing interacts with a directly adjacent element -- this is
10817        different to element changes that affect other elements to change on the
10818        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10819     boolean check_trigger_element =
10820       (trigger_event == CE_TOUCHING_X ||
10821        trigger_event == CE_HITTING_X ||
10822        trigger_event == CE_HIT_BY_X ||
10823        trigger_event == CE_DIGGING_X); // this one was forgotten until 3.2.3
10824
10825     if (change->can_change_or_has_action &&
10826         change->has_event[trigger_event] &&
10827         change->trigger_side & trigger_side &&
10828         change->trigger_player & trigger_player &&
10829         (!check_trigger_element ||
10830          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10831     {
10832       change->actual_trigger_element = trigger_element;
10833       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10834       change->actual_trigger_player_bits = trigger_player;
10835       change->actual_trigger_side = trigger_side;
10836       change->actual_trigger_ce_value = CustomValue[x][y];
10837       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10838
10839       // special case: trigger element not at (x,y) position for some events
10840       if (check_trigger_element)
10841       {
10842         static struct
10843         {
10844           int dx, dy;
10845         } move_xy[] =
10846           {
10847             {  0,  0 },
10848             { -1,  0 },
10849             { +1,  0 },
10850             {  0,  0 },
10851             {  0, -1 },
10852             {  0,  0 }, { 0, 0 }, { 0, 0 },
10853             {  0, +1 }
10854           };
10855
10856         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10857         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10858
10859         change->actual_trigger_ce_value = CustomValue[xx][yy];
10860         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10861       }
10862
10863       if (change->can_change && !change_done)
10864       {
10865         ChangeDelay[x][y] = 1;
10866         ChangeEvent[x][y] = trigger_event;
10867
10868         HandleElementChange(x, y, p);
10869
10870         change_done = TRUE;
10871       }
10872       else if (change->has_action)
10873       {
10874         ExecuteCustomElementAction(x, y, element, p);
10875         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10876       }
10877     }
10878   }
10879
10880   RECURSION_LOOP_DETECTION_END();
10881
10882   return change_done;
10883 }
10884
10885 static void PlayPlayerSound(struct PlayerInfo *player)
10886 {
10887   int jx = player->jx, jy = player->jy;
10888   int sound_element = player->artwork_element;
10889   int last_action = player->last_action_waiting;
10890   int action = player->action_waiting;
10891
10892   if (player->is_waiting)
10893   {
10894     if (action != last_action)
10895       PlayLevelSoundElementAction(jx, jy, sound_element, action);
10896     else
10897       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10898   }
10899   else
10900   {
10901     if (action != last_action)
10902       StopSound(element_info[sound_element].sound[last_action]);
10903
10904     if (last_action == ACTION_SLEEPING)
10905       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10906   }
10907 }
10908
10909 static void PlayAllPlayersSound(void)
10910 {
10911   int i;
10912
10913   for (i = 0; i < MAX_PLAYERS; i++)
10914     if (stored_player[i].active)
10915       PlayPlayerSound(&stored_player[i]);
10916 }
10917
10918 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10919 {
10920   boolean last_waiting = player->is_waiting;
10921   int move_dir = player->MovDir;
10922
10923   player->dir_waiting = move_dir;
10924   player->last_action_waiting = player->action_waiting;
10925
10926   if (is_waiting)
10927   {
10928     if (!last_waiting)          // not waiting -> waiting
10929     {
10930       player->is_waiting = TRUE;
10931
10932       player->frame_counter_bored =
10933         FrameCounter +
10934         game.player_boring_delay_fixed +
10935         GetSimpleRandom(game.player_boring_delay_random);
10936       player->frame_counter_sleeping =
10937         FrameCounter +
10938         game.player_sleeping_delay_fixed +
10939         GetSimpleRandom(game.player_sleeping_delay_random);
10940
10941       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10942     }
10943
10944     if (game.player_sleeping_delay_fixed +
10945         game.player_sleeping_delay_random > 0 &&
10946         player->anim_delay_counter == 0 &&
10947         player->post_delay_counter == 0 &&
10948         FrameCounter >= player->frame_counter_sleeping)
10949       player->is_sleeping = TRUE;
10950     else if (game.player_boring_delay_fixed +
10951              game.player_boring_delay_random > 0 &&
10952              FrameCounter >= player->frame_counter_bored)
10953       player->is_bored = TRUE;
10954
10955     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10956                               player->is_bored ? ACTION_BORING :
10957                               ACTION_WAITING);
10958
10959     if (player->is_sleeping && player->use_murphy)
10960     {
10961       // special case for sleeping Murphy when leaning against non-free tile
10962
10963       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10964           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10965            !IS_MOVING(player->jx - 1, player->jy)))
10966         move_dir = MV_LEFT;
10967       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10968                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10969                 !IS_MOVING(player->jx + 1, player->jy)))
10970         move_dir = MV_RIGHT;
10971       else
10972         player->is_sleeping = FALSE;
10973
10974       player->dir_waiting = move_dir;
10975     }
10976
10977     if (player->is_sleeping)
10978     {
10979       if (player->num_special_action_sleeping > 0)
10980       {
10981         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10982         {
10983           int last_special_action = player->special_action_sleeping;
10984           int num_special_action = player->num_special_action_sleeping;
10985           int special_action =
10986             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10987              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10988              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10989              last_special_action + 1 : ACTION_SLEEPING);
10990           int special_graphic =
10991             el_act_dir2img(player->artwork_element, special_action, move_dir);
10992
10993           player->anim_delay_counter =
10994             graphic_info[special_graphic].anim_delay_fixed +
10995             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10996           player->post_delay_counter =
10997             graphic_info[special_graphic].post_delay_fixed +
10998             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10999
11000           player->special_action_sleeping = special_action;
11001         }
11002
11003         if (player->anim_delay_counter > 0)
11004         {
11005           player->action_waiting = player->special_action_sleeping;
11006           player->anim_delay_counter--;
11007         }
11008         else if (player->post_delay_counter > 0)
11009         {
11010           player->post_delay_counter--;
11011         }
11012       }
11013     }
11014     else if (player->is_bored)
11015     {
11016       if (player->num_special_action_bored > 0)
11017       {
11018         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11019         {
11020           int special_action =
11021             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11022           int special_graphic =
11023             el_act_dir2img(player->artwork_element, special_action, move_dir);
11024
11025           player->anim_delay_counter =
11026             graphic_info[special_graphic].anim_delay_fixed +
11027             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11028           player->post_delay_counter =
11029             graphic_info[special_graphic].post_delay_fixed +
11030             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11031
11032           player->special_action_bored = special_action;
11033         }
11034
11035         if (player->anim_delay_counter > 0)
11036         {
11037           player->action_waiting = player->special_action_bored;
11038           player->anim_delay_counter--;
11039         }
11040         else if (player->post_delay_counter > 0)
11041         {
11042           player->post_delay_counter--;
11043         }
11044       }
11045     }
11046   }
11047   else if (last_waiting)        // waiting -> not waiting
11048   {
11049     player->is_waiting = FALSE;
11050     player->is_bored = FALSE;
11051     player->is_sleeping = FALSE;
11052
11053     player->frame_counter_bored = -1;
11054     player->frame_counter_sleeping = -1;
11055
11056     player->anim_delay_counter = 0;
11057     player->post_delay_counter = 0;
11058
11059     player->dir_waiting = player->MovDir;
11060     player->action_waiting = ACTION_DEFAULT;
11061
11062     player->special_action_bored = ACTION_DEFAULT;
11063     player->special_action_sleeping = ACTION_DEFAULT;
11064   }
11065 }
11066
11067 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
11068 {
11069   if ((!player->is_moving  && player->was_moving) ||
11070       (player->MovPos == 0 && player->was_moving) ||
11071       (player->is_snapping && !player->was_snapping) ||
11072       (player->is_dropping && !player->was_dropping))
11073   {
11074     if (!CheckSaveEngineSnapshotToList())
11075       return;
11076
11077     player->was_moving = FALSE;
11078     player->was_snapping = TRUE;
11079     player->was_dropping = TRUE;
11080   }
11081   else
11082   {
11083     if (player->is_moving)
11084       player->was_moving = TRUE;
11085
11086     if (!player->is_snapping)
11087       player->was_snapping = FALSE;
11088
11089     if (!player->is_dropping)
11090       player->was_dropping = FALSE;
11091   }
11092 }
11093
11094 static void CheckSingleStepMode(struct PlayerInfo *player)
11095 {
11096   if (tape.single_step && tape.recording && !tape.pausing)
11097   {
11098     /* as it is called "single step mode", just return to pause mode when the
11099        player stopped moving after one tile (or never starts moving at all) */
11100     if (!player->is_moving &&
11101         !player->is_pushing &&
11102         !player->is_dropping_pressed)
11103       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11104   }
11105
11106   CheckSaveEngineSnapshot(player);
11107 }
11108
11109 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11110 {
11111   int left      = player_action & JOY_LEFT;
11112   int right     = player_action & JOY_RIGHT;
11113   int up        = player_action & JOY_UP;
11114   int down      = player_action & JOY_DOWN;
11115   int button1   = player_action & JOY_BUTTON_1;
11116   int button2   = player_action & JOY_BUTTON_2;
11117   int dx        = (left ? -1 : right ? 1 : 0);
11118   int dy        = (up   ? -1 : down  ? 1 : 0);
11119
11120   if (!player->active || tape.pausing)
11121     return 0;
11122
11123   if (player_action)
11124   {
11125     if (button1)
11126       SnapField(player, dx, dy);
11127     else
11128     {
11129       if (button2)
11130         DropElement(player);
11131
11132       MovePlayer(player, dx, dy);
11133     }
11134
11135     CheckSingleStepMode(player);
11136
11137     SetPlayerWaiting(player, FALSE);
11138
11139     return player_action;
11140   }
11141   else
11142   {
11143     // no actions for this player (no input at player's configured device)
11144
11145     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11146     SnapField(player, 0, 0);
11147     CheckGravityMovementWhenNotMoving(player);
11148
11149     if (player->MovPos == 0)
11150       SetPlayerWaiting(player, TRUE);
11151
11152     if (player->MovPos == 0)    // needed for tape.playing
11153       player->is_moving = FALSE;
11154
11155     player->is_dropping = FALSE;
11156     player->is_dropping_pressed = FALSE;
11157     player->drop_pressed_delay = 0;
11158
11159     CheckSingleStepMode(player);
11160
11161     return 0;
11162   }
11163 }
11164
11165 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11166                                          byte *tape_action)
11167 {
11168   if (!tape.use_mouse)
11169     return;
11170
11171   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11172   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11173   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11174 }
11175
11176 static void SetTapeActionFromMouseAction(byte *tape_action,
11177                                          struct MouseActionInfo *mouse_action)
11178 {
11179   if (!tape.use_mouse)
11180     return;
11181
11182   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11183   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11184   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11185 }
11186
11187 static void CheckLevelSolved(void)
11188 {
11189   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11190   {
11191     if (game_em.level_solved &&
11192         !game_em.game_over)                             // game won
11193     {
11194       LevelSolved();
11195
11196       game_em.game_over = TRUE;
11197
11198       game.all_players_gone = TRUE;
11199     }
11200
11201     if (game_em.game_over)                              // game lost
11202       game.all_players_gone = TRUE;
11203   }
11204   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11205   {
11206     if (game_sp.level_solved &&
11207         !game_sp.game_over)                             // game won
11208     {
11209       LevelSolved();
11210
11211       game_sp.game_over = TRUE;
11212
11213       game.all_players_gone = TRUE;
11214     }
11215
11216     if (game_sp.game_over)                              // game lost
11217       game.all_players_gone = TRUE;
11218   }
11219   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11220   {
11221     if (game_mm.level_solved &&
11222         !game_mm.game_over)                             // game won
11223     {
11224       LevelSolved();
11225
11226       game_mm.game_over = TRUE;
11227
11228       game.all_players_gone = TRUE;
11229     }
11230
11231     if (game_mm.game_over)                              // game lost
11232       game.all_players_gone = TRUE;
11233   }
11234 }
11235
11236 static void CheckLevelTime(void)
11237 {
11238   int i;
11239
11240   if (TimeFrames >= FRAMES_PER_SECOND)
11241   {
11242     TimeFrames = 0;
11243     TapeTime++;
11244
11245     for (i = 0; i < MAX_PLAYERS; i++)
11246     {
11247       struct PlayerInfo *player = &stored_player[i];
11248
11249       if (SHIELD_ON(player))
11250       {
11251         player->shield_normal_time_left--;
11252
11253         if (player->shield_deadly_time_left > 0)
11254           player->shield_deadly_time_left--;
11255       }
11256     }
11257
11258     if (!game.LevelSolved && !level.use_step_counter)
11259     {
11260       TimePlayed++;
11261
11262       if (TimeLeft > 0)
11263       {
11264         TimeLeft--;
11265
11266         if (TimeLeft <= 10 && setup.time_limit)
11267           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11268
11269         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11270            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11271
11272         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11273
11274         if (!TimeLeft && setup.time_limit)
11275         {
11276           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11277             game_em.lev->killed_out_of_time = TRUE;
11278           else
11279             for (i = 0; i < MAX_PLAYERS; i++)
11280               KillPlayer(&stored_player[i]);
11281         }
11282       }
11283       else if (game.no_time_limit && !game.all_players_gone)
11284       {
11285         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11286       }
11287
11288       game_em.lev->time = (game.no_time_limit ? TimePlayed : TimeLeft);
11289     }
11290
11291     if (tape.recording || tape.playing)
11292       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11293   }
11294
11295   if (tape.recording || tape.playing)
11296     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11297
11298   UpdateAndDisplayGameControlValues();
11299 }
11300
11301 void AdvanceFrameAndPlayerCounters(int player_nr)
11302 {
11303   int i;
11304
11305   // advance frame counters (global frame counter and time frame counter)
11306   FrameCounter++;
11307   TimeFrames++;
11308
11309   // advance player counters (counters for move delay, move animation etc.)
11310   for (i = 0; i < MAX_PLAYERS; i++)
11311   {
11312     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11313     int move_delay_value = stored_player[i].move_delay_value;
11314     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11315
11316     if (!advance_player_counters)       // not all players may be affected
11317       continue;
11318
11319     if (move_frames == 0)       // less than one move per game frame
11320     {
11321       int stepsize = TILEX / move_delay_value;
11322       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11323       int count = (stored_player[i].is_moving ?
11324                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11325
11326       if (count % delay == 0)
11327         move_frames = 1;
11328     }
11329
11330     stored_player[i].Frame += move_frames;
11331
11332     if (stored_player[i].MovPos != 0)
11333       stored_player[i].StepFrame += move_frames;
11334
11335     if (stored_player[i].move_delay > 0)
11336       stored_player[i].move_delay--;
11337
11338     // due to bugs in previous versions, counter must count up, not down
11339     if (stored_player[i].push_delay != -1)
11340       stored_player[i].push_delay++;
11341
11342     if (stored_player[i].drop_delay > 0)
11343       stored_player[i].drop_delay--;
11344
11345     if (stored_player[i].is_dropping_pressed)
11346       stored_player[i].drop_pressed_delay++;
11347   }
11348 }
11349
11350 void StartGameActions(boolean init_network_game, boolean record_tape,
11351                       int random_seed)
11352 {
11353   unsigned int new_random_seed = InitRND(random_seed);
11354
11355   if (record_tape)
11356     TapeStartRecording(new_random_seed);
11357
11358   if (init_network_game)
11359   {
11360     SendToServer_LevelFile();
11361     SendToServer_StartPlaying();
11362
11363     return;
11364   }
11365
11366   InitGame();
11367 }
11368
11369 static void GameActionsExt(void)
11370 {
11371 #if 0
11372   static unsigned int game_frame_delay = 0;
11373 #endif
11374   unsigned int game_frame_delay_value;
11375   byte *recorded_player_action;
11376   byte summarized_player_action = 0;
11377   byte tape_action[MAX_TAPE_ACTIONS] = { 0 };
11378   int i;
11379
11380   // detect endless loops, caused by custom element programming
11381   if (recursion_loop_detected && recursion_loop_depth == 0)
11382   {
11383     char *message = getStringCat3("Internal Error! Element ",
11384                                   EL_NAME(recursion_loop_element),
11385                                   " caused endless loop! Quit the game?");
11386
11387     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11388           EL_NAME(recursion_loop_element));
11389
11390     RequestQuitGameExt(FALSE, level_editor_test_game, message);
11391
11392     recursion_loop_detected = FALSE;    // if game should be continued
11393
11394     free(message);
11395
11396     return;
11397   }
11398
11399   if (game.restart_level)
11400     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11401
11402   CheckLevelSolved();
11403
11404   if (game.LevelSolved && !game.LevelSolved_GameEnd)
11405     GameWon();
11406
11407   if (game.all_players_gone && !TAPE_IS_STOPPED(tape))
11408     TapeStop();
11409
11410   if (game_status != GAME_MODE_PLAYING)         // status might have changed
11411     return;
11412
11413   game_frame_delay_value =
11414     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11415
11416   if (tape.playing && tape.warp_forward && !tape.pausing)
11417     game_frame_delay_value = 0;
11418
11419   SetVideoFrameDelay(game_frame_delay_value);
11420
11421   // (de)activate virtual buttons depending on current game status
11422   if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
11423   {
11424     if (game.all_players_gone)  // if no players there to be controlled anymore
11425       SetOverlayActive(FALSE);
11426     else if (!tape.playing)     // if game continues after tape stopped playing
11427       SetOverlayActive(TRUE);
11428   }
11429
11430 #if 0
11431 #if 0
11432   // ---------- main game synchronization point ----------
11433
11434   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11435
11436   printf("::: skip == %d\n", skip);
11437
11438 #else
11439   // ---------- main game synchronization point ----------
11440
11441   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11442 #endif
11443 #endif
11444
11445   if (network_playing && !network_player_action_received)
11446   {
11447     // try to get network player actions in time
11448
11449     // last chance to get network player actions without main loop delay
11450     HandleNetworking();
11451
11452     // game was quit by network peer
11453     if (game_status != GAME_MODE_PLAYING)
11454       return;
11455
11456     // check if network player actions still missing and game still running
11457     if (!network_player_action_received && !checkGameEnded())
11458       return;           // failed to get network player actions in time
11459
11460     // do not yet reset "network_player_action_received" (for tape.pausing)
11461   }
11462
11463   if (tape.pausing)
11464     return;
11465
11466   // at this point we know that we really continue executing the game
11467
11468   network_player_action_received = FALSE;
11469
11470   // when playing tape, read previously recorded player input from tape data
11471   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11472
11473   local_player->effective_mouse_action = local_player->mouse_action;
11474
11475   if (recorded_player_action != NULL)
11476     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11477                                  recorded_player_action);
11478
11479   // TapePlayAction() may return NULL when toggling to "pause before death"
11480   if (tape.pausing)
11481     return;
11482
11483   if (tape.set_centered_player)
11484   {
11485     game.centered_player_nr_next = tape.centered_player_nr_next;
11486     game.set_centered_player = TRUE;
11487   }
11488
11489   for (i = 0; i < MAX_PLAYERS; i++)
11490   {
11491     summarized_player_action |= stored_player[i].action;
11492
11493     if (!network_playing && (game.team_mode || tape.playing))
11494       stored_player[i].effective_action = stored_player[i].action;
11495   }
11496
11497   if (network_playing && !checkGameEnded())
11498     SendToServer_MovePlayer(summarized_player_action);
11499
11500   // summarize all actions at local players mapped input device position
11501   // (this allows using different input devices in single player mode)
11502   if (!network.enabled && !game.team_mode)
11503     stored_player[map_player_action[local_player->index_nr]].effective_action =
11504       summarized_player_action;
11505
11506   // summarize all actions at centered player in local team mode
11507   if (tape.recording &&
11508       setup.team_mode && !network.enabled &&
11509       setup.input_on_focus &&
11510       game.centered_player_nr != -1)
11511   {
11512     for (i = 0; i < MAX_PLAYERS; i++)
11513       stored_player[map_player_action[i]].effective_action =
11514         (i == game.centered_player_nr ? summarized_player_action : 0);
11515   }
11516
11517   if (recorded_player_action != NULL)
11518     for (i = 0; i < MAX_PLAYERS; i++)
11519       stored_player[i].effective_action = recorded_player_action[i];
11520
11521   for (i = 0; i < MAX_PLAYERS; i++)
11522   {
11523     tape_action[i] = stored_player[i].effective_action;
11524
11525     /* (this may happen in the RND game engine if a player was not present on
11526        the playfield on level start, but appeared later from a custom element */
11527     if (setup.team_mode &&
11528         tape.recording &&
11529         tape_action[i] &&
11530         !tape.player_participates[i])
11531       tape.player_participates[i] = TRUE;
11532   }
11533
11534   SetTapeActionFromMouseAction(tape_action,
11535                                &local_player->effective_mouse_action);
11536
11537   // only record actions from input devices, but not programmed actions
11538   if (tape.recording)
11539     TapeRecordAction(tape_action);
11540
11541   // remember if game was played (especially after tape stopped playing)
11542   if (!tape.playing && summarized_player_action)
11543     game.GamePlayed = TRUE;
11544
11545 #if USE_NEW_PLAYER_ASSIGNMENTS
11546   // !!! also map player actions in single player mode !!!
11547   // if (game.team_mode)
11548   if (1)
11549   {
11550     byte mapped_action[MAX_PLAYERS];
11551
11552 #if DEBUG_PLAYER_ACTIONS
11553     printf(":::");
11554     for (i = 0; i < MAX_PLAYERS; i++)
11555       printf(" %d, ", stored_player[i].effective_action);
11556 #endif
11557
11558     for (i = 0; i < MAX_PLAYERS; i++)
11559       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11560
11561     for (i = 0; i < MAX_PLAYERS; i++)
11562       stored_player[i].effective_action = mapped_action[i];
11563
11564 #if DEBUG_PLAYER_ACTIONS
11565     printf(" =>");
11566     for (i = 0; i < MAX_PLAYERS; i++)
11567       printf(" %d, ", stored_player[i].effective_action);
11568     printf("\n");
11569 #endif
11570   }
11571 #if DEBUG_PLAYER_ACTIONS
11572   else
11573   {
11574     printf(":::");
11575     for (i = 0; i < MAX_PLAYERS; i++)
11576       printf(" %d, ", stored_player[i].effective_action);
11577     printf("\n");
11578   }
11579 #endif
11580 #endif
11581
11582   for (i = 0; i < MAX_PLAYERS; i++)
11583   {
11584     // allow engine snapshot in case of changed movement attempt
11585     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11586         (stored_player[i].effective_action & KEY_MOTION))
11587       game.snapshot.changed_action = TRUE;
11588
11589     // allow engine snapshot in case of snapping/dropping attempt
11590     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11591         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11592       game.snapshot.changed_action = TRUE;
11593
11594     game.snapshot.last_action[i] = stored_player[i].effective_action;
11595   }
11596
11597   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11598   {
11599     GameActions_EM_Main();
11600   }
11601   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11602   {
11603     GameActions_SP_Main();
11604   }
11605   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11606   {
11607     GameActions_MM_Main();
11608   }
11609   else
11610   {
11611     GameActions_RND_Main();
11612   }
11613
11614   BlitScreenToBitmap(backbuffer);
11615
11616   CheckLevelSolved();
11617   CheckLevelTime();
11618
11619   AdvanceFrameAndPlayerCounters(-1);    // advance counters for all players
11620
11621   if (global.show_frames_per_second)
11622   {
11623     static unsigned int fps_counter = 0;
11624     static int fps_frames = 0;
11625     unsigned int fps_delay_ms = Counter() - fps_counter;
11626
11627     fps_frames++;
11628
11629     if (fps_delay_ms >= 500)    // calculate FPS every 0.5 seconds
11630     {
11631       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11632
11633       fps_frames = 0;
11634       fps_counter = Counter();
11635
11636       // always draw FPS to screen after FPS value was updated
11637       redraw_mask |= REDRAW_FPS;
11638     }
11639
11640     // only draw FPS if no screen areas are deactivated (invisible warp mode)
11641     if (GetDrawDeactivationMask() == REDRAW_NONE)
11642       redraw_mask |= REDRAW_FPS;
11643   }
11644 }
11645
11646 static void GameActions_CheckSaveEngineSnapshot(void)
11647 {
11648   if (!game.snapshot.save_snapshot)
11649     return;
11650
11651   // clear flag for saving snapshot _before_ saving snapshot
11652   game.snapshot.save_snapshot = FALSE;
11653
11654   SaveEngineSnapshotToList();
11655 }
11656
11657 void GameActions(void)
11658 {
11659   GameActionsExt();
11660
11661   GameActions_CheckSaveEngineSnapshot();
11662 }
11663
11664 void GameActions_EM_Main(void)
11665 {
11666   byte effective_action[MAX_PLAYERS];
11667   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11668   int i;
11669
11670   for (i = 0; i < MAX_PLAYERS; i++)
11671     effective_action[i] = stored_player[i].effective_action;
11672
11673   GameActions_EM(effective_action, warp_mode);
11674 }
11675
11676 void GameActions_SP_Main(void)
11677 {
11678   byte effective_action[MAX_PLAYERS];
11679   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11680   int i;
11681
11682   for (i = 0; i < MAX_PLAYERS; i++)
11683     effective_action[i] = stored_player[i].effective_action;
11684
11685   GameActions_SP(effective_action, warp_mode);
11686
11687   for (i = 0; i < MAX_PLAYERS; i++)
11688   {
11689     if (stored_player[i].force_dropping)
11690       stored_player[i].action |= KEY_BUTTON_DROP;
11691
11692     stored_player[i].force_dropping = FALSE;
11693   }
11694 }
11695
11696 void GameActions_MM_Main(void)
11697 {
11698   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11699
11700   GameActions_MM(local_player->effective_mouse_action, warp_mode);
11701 }
11702
11703 void GameActions_RND_Main(void)
11704 {
11705   GameActions_RND();
11706 }
11707
11708 void GameActions_RND(void)
11709 {
11710   static struct MouseActionInfo mouse_action_last = { 0 };
11711   struct MouseActionInfo mouse_action = local_player->effective_mouse_action;
11712   int magic_wall_x = 0, magic_wall_y = 0;
11713   int i, x, y, element, graphic, last_gfx_frame;
11714
11715   InitPlayfieldScanModeVars();
11716
11717   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11718   {
11719     SCAN_PLAYFIELD(x, y)
11720     {
11721       ChangeCount[x][y] = 0;
11722       ChangeEvent[x][y] = -1;
11723     }
11724   }
11725
11726   if (game.set_centered_player)
11727   {
11728     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11729
11730     // switching to "all players" only possible if all players fit to screen
11731     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11732     {
11733       game.centered_player_nr_next = game.centered_player_nr;
11734       game.set_centered_player = FALSE;
11735     }
11736
11737     // do not switch focus to non-existing (or non-active) player
11738     if (game.centered_player_nr_next >= 0 &&
11739         !stored_player[game.centered_player_nr_next].active)
11740     {
11741       game.centered_player_nr_next = game.centered_player_nr;
11742       game.set_centered_player = FALSE;
11743     }
11744   }
11745
11746   if (game.set_centered_player &&
11747       ScreenMovPos == 0)        // screen currently aligned at tile position
11748   {
11749     int sx, sy;
11750
11751     if (game.centered_player_nr_next == -1)
11752     {
11753       setScreenCenteredToAllPlayers(&sx, &sy);
11754     }
11755     else
11756     {
11757       sx = stored_player[game.centered_player_nr_next].jx;
11758       sy = stored_player[game.centered_player_nr_next].jy;
11759     }
11760
11761     game.centered_player_nr = game.centered_player_nr_next;
11762     game.set_centered_player = FALSE;
11763
11764     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11765     DrawGameDoorValues();
11766   }
11767
11768   for (i = 0; i < MAX_PLAYERS; i++)
11769   {
11770     int actual_player_action = stored_player[i].effective_action;
11771
11772 #if 1
11773     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11774        - rnd_equinox_tetrachloride 048
11775        - rnd_equinox_tetrachloride_ii 096
11776        - rnd_emanuel_schmieg 002
11777        - doctor_sloan_ww 001, 020
11778     */
11779     if (stored_player[i].MovPos == 0)
11780       CheckGravityMovement(&stored_player[i]);
11781 #endif
11782
11783     // overwrite programmed action with tape action
11784     if (stored_player[i].programmed_action)
11785       actual_player_action = stored_player[i].programmed_action;
11786
11787     PlayerActions(&stored_player[i], actual_player_action);
11788
11789     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11790   }
11791
11792   ScrollScreen(NULL, SCROLL_GO_ON);
11793
11794   /* for backwards compatibility, the following code emulates a fixed bug that
11795      occured when pushing elements (causing elements that just made their last
11796      pushing step to already (if possible) make their first falling step in the
11797      same game frame, which is bad); this code is also needed to use the famous
11798      "spring push bug" which is used in older levels and might be wanted to be
11799      used also in newer levels, but in this case the buggy pushing code is only
11800      affecting the "spring" element and no other elements */
11801
11802   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11803   {
11804     for (i = 0; i < MAX_PLAYERS; i++)
11805     {
11806       struct PlayerInfo *player = &stored_player[i];
11807       int x = player->jx;
11808       int y = player->jy;
11809
11810       if (player->active && player->is_pushing && player->is_moving &&
11811           IS_MOVING(x, y) &&
11812           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11813            Feld[x][y] == EL_SPRING))
11814       {
11815         ContinueMoving(x, y);
11816
11817         // continue moving after pushing (this is actually a bug)
11818         if (!IS_MOVING(x, y))
11819           Stop[x][y] = FALSE;
11820       }
11821     }
11822   }
11823
11824   SCAN_PLAYFIELD(x, y)
11825   {
11826     Last[x][y] = Feld[x][y];
11827
11828     ChangeCount[x][y] = 0;
11829     ChangeEvent[x][y] = -1;
11830
11831     // this must be handled before main playfield loop
11832     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11833     {
11834       MovDelay[x][y]--;
11835       if (MovDelay[x][y] <= 0)
11836         RemoveField(x, y);
11837     }
11838
11839     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11840     {
11841       MovDelay[x][y]--;
11842       if (MovDelay[x][y] <= 0)
11843       {
11844         RemoveField(x, y);
11845         TEST_DrawLevelField(x, y);
11846
11847         TestIfElementTouchesCustomElement(x, y);        // for empty space
11848       }
11849     }
11850
11851 #if DEBUG
11852     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11853     {
11854       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11855       printf("GameActions(): This should never happen!\n");
11856
11857       ChangePage[x][y] = -1;
11858     }
11859 #endif
11860
11861     Stop[x][y] = FALSE;
11862     if (WasJustMoving[x][y] > 0)
11863       WasJustMoving[x][y]--;
11864     if (WasJustFalling[x][y] > 0)
11865       WasJustFalling[x][y]--;
11866     if (CheckCollision[x][y] > 0)
11867       CheckCollision[x][y]--;
11868     if (CheckImpact[x][y] > 0)
11869       CheckImpact[x][y]--;
11870
11871     GfxFrame[x][y]++;
11872
11873     /* reset finished pushing action (not done in ContinueMoving() to allow
11874        continuous pushing animation for elements with zero push delay) */
11875     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11876     {
11877       ResetGfxAnimation(x, y);
11878       TEST_DrawLevelField(x, y);
11879     }
11880
11881 #if DEBUG
11882     if (IS_BLOCKED(x, y))
11883     {
11884       int oldx, oldy;
11885
11886       Blocked2Moving(x, y, &oldx, &oldy);
11887       if (!IS_MOVING(oldx, oldy))
11888       {
11889         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11890         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11891         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11892         printf("GameActions(): This should never happen!\n");
11893       }
11894     }
11895 #endif
11896   }
11897
11898   if (mouse_action.button)
11899   {
11900     int new_button = (mouse_action.button && mouse_action_last.button == 0);
11901
11902     x = local_player->mouse_action.lx;
11903     y = local_player->mouse_action.ly;
11904     element = Feld[x][y];
11905
11906     if (new_button)
11907     {
11908       CheckElementChange(x, y, element, EL_UNDEFINED, CE_CLICKED_BY_MOUSE);
11909       CheckTriggeredElementChange(x, y, element, CE_MOUSE_CLICKED_ON_X);
11910     }
11911
11912     CheckElementChange(x, y, element, EL_UNDEFINED, CE_PRESSED_BY_MOUSE);
11913     CheckTriggeredElementChange(x, y, element, CE_MOUSE_PRESSED_ON_X);
11914   }
11915
11916   SCAN_PLAYFIELD(x, y)
11917   {
11918     element = Feld[x][y];
11919     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11920     last_gfx_frame = GfxFrame[x][y];
11921
11922     ResetGfxFrame(x, y);
11923
11924     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
11925       DrawLevelGraphicAnimation(x, y, graphic);
11926
11927     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11928         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11929       ResetRandomAnimationValue(x, y);
11930
11931     SetRandomAnimationValue(x, y);
11932
11933     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11934
11935     if (IS_INACTIVE(element))
11936     {
11937       if (IS_ANIMATED(graphic))
11938         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11939
11940       continue;
11941     }
11942
11943     // this may take place after moving, so 'element' may have changed
11944     if (IS_CHANGING(x, y) &&
11945         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11946     {
11947       int page = element_info[element].event_page_nr[CE_DELAY];
11948
11949       HandleElementChange(x, y, page);
11950
11951       element = Feld[x][y];
11952       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11953     }
11954
11955     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11956     {
11957       StartMoving(x, y);
11958
11959       element = Feld[x][y];
11960       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11961
11962       if (IS_ANIMATED(graphic) &&
11963           !IS_MOVING(x, y) &&
11964           !Stop[x][y])
11965         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11966
11967       if (IS_GEM(element) || element == EL_SP_INFOTRON)
11968         TEST_DrawTwinkleOnField(x, y);
11969     }
11970     else if (element == EL_ACID)
11971     {
11972       if (!Stop[x][y])
11973         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11974     }
11975     else if ((element == EL_EXIT_OPEN ||
11976               element == EL_EM_EXIT_OPEN ||
11977               element == EL_SP_EXIT_OPEN ||
11978               element == EL_STEEL_EXIT_OPEN ||
11979               element == EL_EM_STEEL_EXIT_OPEN ||
11980               element == EL_SP_TERMINAL ||
11981               element == EL_SP_TERMINAL_ACTIVE ||
11982               element == EL_EXTRA_TIME ||
11983               element == EL_SHIELD_NORMAL ||
11984               element == EL_SHIELD_DEADLY) &&
11985              IS_ANIMATED(graphic))
11986       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11987     else if (IS_MOVING(x, y))
11988       ContinueMoving(x, y);
11989     else if (IS_ACTIVE_BOMB(element))
11990       CheckDynamite(x, y);
11991     else if (element == EL_AMOEBA_GROWING)
11992       AmoebeWaechst(x, y);
11993     else if (element == EL_AMOEBA_SHRINKING)
11994       AmoebaDisappearing(x, y);
11995
11996 #if !USE_NEW_AMOEBA_CODE
11997     else if (IS_AMOEBALIVE(element))
11998       AmoebeAbleger(x, y);
11999 #endif
12000
12001     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12002       Life(x, y);
12003     else if (element == EL_EXIT_CLOSED)
12004       CheckExit(x, y);
12005     else if (element == EL_EM_EXIT_CLOSED)
12006       CheckExitEM(x, y);
12007     else if (element == EL_STEEL_EXIT_CLOSED)
12008       CheckExitSteel(x, y);
12009     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12010       CheckExitSteelEM(x, y);
12011     else if (element == EL_SP_EXIT_CLOSED)
12012       CheckExitSP(x, y);
12013     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12014              element == EL_EXPANDABLE_STEELWALL_GROWING)
12015       MauerWaechst(x, y);
12016     else if (element == EL_EXPANDABLE_WALL ||
12017              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12018              element == EL_EXPANDABLE_WALL_VERTICAL ||
12019              element == EL_EXPANDABLE_WALL_ANY ||
12020              element == EL_BD_EXPANDABLE_WALL)
12021       MauerAbleger(x, y);
12022     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12023              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12024              element == EL_EXPANDABLE_STEELWALL_ANY)
12025       MauerAblegerStahl(x, y);
12026     else if (element == EL_FLAMES)
12027       CheckForDragon(x, y);
12028     else if (element == EL_EXPLOSION)
12029       ; // drawing of correct explosion animation is handled separately
12030     else if (element == EL_ELEMENT_SNAPPING ||
12031              element == EL_DIAGONAL_SHRINKING ||
12032              element == EL_DIAGONAL_GROWING)
12033     {
12034       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12035
12036       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12037     }
12038     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12039       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12040
12041     if (IS_BELT_ACTIVE(element))
12042       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12043
12044     if (game.magic_wall_active)
12045     {
12046       int jx = local_player->jx, jy = local_player->jy;
12047
12048       // play the element sound at the position nearest to the player
12049       if ((element == EL_MAGIC_WALL_FULL ||
12050            element == EL_MAGIC_WALL_ACTIVE ||
12051            element == EL_MAGIC_WALL_EMPTYING ||
12052            element == EL_BD_MAGIC_WALL_FULL ||
12053            element == EL_BD_MAGIC_WALL_ACTIVE ||
12054            element == EL_BD_MAGIC_WALL_EMPTYING ||
12055            element == EL_DC_MAGIC_WALL_FULL ||
12056            element == EL_DC_MAGIC_WALL_ACTIVE ||
12057            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12058           ABS(x - jx) + ABS(y - jy) <
12059           ABS(magic_wall_x - jx) + ABS(magic_wall_y - jy))
12060       {
12061         magic_wall_x = x;
12062         magic_wall_y = y;
12063       }
12064     }
12065   }
12066
12067 #if USE_NEW_AMOEBA_CODE
12068   // new experimental amoeba growth stuff
12069   if (!(FrameCounter % 8))
12070   {
12071     static unsigned int random = 1684108901;
12072
12073     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12074     {
12075       x = RND(lev_fieldx);
12076       y = RND(lev_fieldy);
12077       element = Feld[x][y];
12078
12079       if (!IS_PLAYER(x,y) &&
12080           (element == EL_EMPTY ||
12081            CAN_GROW_INTO(element) ||
12082            element == EL_QUICKSAND_EMPTY ||
12083            element == EL_QUICKSAND_FAST_EMPTY ||
12084            element == EL_ACID_SPLASH_LEFT ||
12085            element == EL_ACID_SPLASH_RIGHT))
12086       {
12087         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
12088             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
12089             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
12090             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
12091           Feld[x][y] = EL_AMOEBA_DROP;
12092       }
12093
12094       random = random * 129 + 1;
12095     }
12096   }
12097 #endif
12098
12099   game.explosions_delayed = FALSE;
12100
12101   SCAN_PLAYFIELD(x, y)
12102   {
12103     element = Feld[x][y];
12104
12105     if (ExplodeField[x][y])
12106       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12107     else if (element == EL_EXPLOSION)
12108       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12109
12110     ExplodeField[x][y] = EX_TYPE_NONE;
12111   }
12112
12113   game.explosions_delayed = TRUE;
12114
12115   if (game.magic_wall_active)
12116   {
12117     if (!(game.magic_wall_time_left % 4))
12118     {
12119       int element = Feld[magic_wall_x][magic_wall_y];
12120
12121       if (element == EL_BD_MAGIC_WALL_FULL ||
12122           element == EL_BD_MAGIC_WALL_ACTIVE ||
12123           element == EL_BD_MAGIC_WALL_EMPTYING)
12124         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12125       else if (element == EL_DC_MAGIC_WALL_FULL ||
12126                element == EL_DC_MAGIC_WALL_ACTIVE ||
12127                element == EL_DC_MAGIC_WALL_EMPTYING)
12128         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12129       else
12130         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12131     }
12132
12133     if (game.magic_wall_time_left > 0)
12134     {
12135       game.magic_wall_time_left--;
12136
12137       if (!game.magic_wall_time_left)
12138       {
12139         SCAN_PLAYFIELD(x, y)
12140         {
12141           element = Feld[x][y];
12142
12143           if (element == EL_MAGIC_WALL_ACTIVE ||
12144               element == EL_MAGIC_WALL_FULL)
12145           {
12146             Feld[x][y] = EL_MAGIC_WALL_DEAD;
12147             TEST_DrawLevelField(x, y);
12148           }
12149           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12150                    element == EL_BD_MAGIC_WALL_FULL)
12151           {
12152             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
12153             TEST_DrawLevelField(x, y);
12154           }
12155           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12156                    element == EL_DC_MAGIC_WALL_FULL)
12157           {
12158             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
12159             TEST_DrawLevelField(x, y);
12160           }
12161         }
12162
12163         game.magic_wall_active = FALSE;
12164       }
12165     }
12166   }
12167
12168   if (game.light_time_left > 0)
12169   {
12170     game.light_time_left--;
12171
12172     if (game.light_time_left == 0)
12173       RedrawAllLightSwitchesAndInvisibleElements();
12174   }
12175
12176   if (game.timegate_time_left > 0)
12177   {
12178     game.timegate_time_left--;
12179
12180     if (game.timegate_time_left == 0)
12181       CloseAllOpenTimegates();
12182   }
12183
12184   if (game.lenses_time_left > 0)
12185   {
12186     game.lenses_time_left--;
12187
12188     if (game.lenses_time_left == 0)
12189       RedrawAllInvisibleElementsForLenses();
12190   }
12191
12192   if (game.magnify_time_left > 0)
12193   {
12194     game.magnify_time_left--;
12195
12196     if (game.magnify_time_left == 0)
12197       RedrawAllInvisibleElementsForMagnifier();
12198   }
12199
12200   for (i = 0; i < MAX_PLAYERS; i++)
12201   {
12202     struct PlayerInfo *player = &stored_player[i];
12203
12204     if (SHIELD_ON(player))
12205     {
12206       if (player->shield_deadly_time_left)
12207         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12208       else if (player->shield_normal_time_left)
12209         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12210     }
12211   }
12212
12213 #if USE_DELAYED_GFX_REDRAW
12214   SCAN_PLAYFIELD(x, y)
12215   {
12216     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12217     {
12218       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12219          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12220
12221       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12222         DrawLevelField(x, y);
12223
12224       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12225         DrawLevelFieldCrumbled(x, y);
12226
12227       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12228         DrawLevelFieldCrumbledNeighbours(x, y);
12229
12230       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12231         DrawTwinkleOnField(x, y);
12232     }
12233
12234     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12235   }
12236 #endif
12237
12238   DrawAllPlayers();
12239   PlayAllPlayersSound();
12240
12241   for (i = 0; i < MAX_PLAYERS; i++)
12242   {
12243     struct PlayerInfo *player = &stored_player[i];
12244
12245     if (player->show_envelope != 0 && (!player->active ||
12246                                        player->MovPos == 0))
12247     {
12248       ShowEnvelope(player->show_envelope - EL_ENVELOPE_1);
12249
12250       player->show_envelope = 0;
12251     }
12252   }
12253
12254   // use random number generator in every frame to make it less predictable
12255   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12256     RND(1);
12257
12258   mouse_action_last = mouse_action;
12259 }
12260
12261 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12262 {
12263   int min_x = x, min_y = y, max_x = x, max_y = y;
12264   int i;
12265
12266   for (i = 0; i < MAX_PLAYERS; i++)
12267   {
12268     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12269
12270     if (!stored_player[i].active || &stored_player[i] == player)
12271       continue;
12272
12273     min_x = MIN(min_x, jx);
12274     min_y = MIN(min_y, jy);
12275     max_x = MAX(max_x, jx);
12276     max_y = MAX(max_y, jy);
12277   }
12278
12279   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12280 }
12281
12282 static boolean AllPlayersInVisibleScreen(void)
12283 {
12284   int i;
12285
12286   for (i = 0; i < MAX_PLAYERS; i++)
12287   {
12288     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12289
12290     if (!stored_player[i].active)
12291       continue;
12292
12293     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12294       return FALSE;
12295   }
12296
12297   return TRUE;
12298 }
12299
12300 void ScrollLevel(int dx, int dy)
12301 {
12302   int scroll_offset = 2 * TILEX_VAR;
12303   int x, y;
12304
12305   BlitBitmap(drawto_field, drawto_field,
12306              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12307              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12308              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12309              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12310              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12311              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12312
12313   if (dx != 0)
12314   {
12315     x = (dx == 1 ? BX1 : BX2);
12316     for (y = BY1; y <= BY2; y++)
12317       DrawScreenField(x, y);
12318   }
12319
12320   if (dy != 0)
12321   {
12322     y = (dy == 1 ? BY1 : BY2);
12323     for (x = BX1; x <= BX2; x++)
12324       DrawScreenField(x, y);
12325   }
12326
12327   redraw_mask |= REDRAW_FIELD;
12328 }
12329
12330 static boolean canFallDown(struct PlayerInfo *player)
12331 {
12332   int jx = player->jx, jy = player->jy;
12333
12334   return (IN_LEV_FIELD(jx, jy + 1) &&
12335           (IS_FREE(jx, jy + 1) ||
12336            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12337           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
12338           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
12339 }
12340
12341 static boolean canPassField(int x, int y, int move_dir)
12342 {
12343   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12344   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12345   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12346   int nextx = x + dx;
12347   int nexty = y + dy;
12348   int element = Feld[x][y];
12349
12350   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12351           !CAN_MOVE(element) &&
12352           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12353           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
12354           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12355 }
12356
12357 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12358 {
12359   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12360   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12361   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12362   int newx = x + dx;
12363   int newy = y + dy;
12364
12365   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12366           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12367           (IS_DIGGABLE(Feld[newx][newy]) ||
12368            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12369            canPassField(newx, newy, move_dir)));
12370 }
12371
12372 static void CheckGravityMovement(struct PlayerInfo *player)
12373 {
12374   if (player->gravity && !player->programmed_action)
12375   {
12376     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12377     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12378     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12379     int jx = player->jx, jy = player->jy;
12380     boolean player_is_moving_to_valid_field =
12381       (!player_is_snapping &&
12382        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12383         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12384     boolean player_can_fall_down = canFallDown(player);
12385
12386     if (player_can_fall_down &&
12387         !player_is_moving_to_valid_field)
12388       player->programmed_action = MV_DOWN;
12389   }
12390 }
12391
12392 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12393 {
12394   return CheckGravityMovement(player);
12395
12396   if (player->gravity && !player->programmed_action)
12397   {
12398     int jx = player->jx, jy = player->jy;
12399     boolean field_under_player_is_free =
12400       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12401     boolean player_is_standing_on_valid_field =
12402       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12403        (IS_WALKABLE(Feld[jx][jy]) &&
12404         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12405
12406     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12407       player->programmed_action = MV_DOWN;
12408   }
12409 }
12410
12411 /*
12412   MovePlayerOneStep()
12413   -----------------------------------------------------------------------------
12414   dx, dy:               direction (non-diagonal) to try to move the player to
12415   real_dx, real_dy:     direction as read from input device (can be diagonal)
12416 */
12417
12418 boolean MovePlayerOneStep(struct PlayerInfo *player,
12419                           int dx, int dy, int real_dx, int real_dy)
12420 {
12421   int jx = player->jx, jy = player->jy;
12422   int new_jx = jx + dx, new_jy = jy + dy;
12423   int can_move;
12424   boolean player_can_move = !player->cannot_move;
12425
12426   if (!player->active || (!dx && !dy))
12427     return MP_NO_ACTION;
12428
12429   player->MovDir = (dx < 0 ? MV_LEFT :
12430                     dx > 0 ? MV_RIGHT :
12431                     dy < 0 ? MV_UP :
12432                     dy > 0 ? MV_DOWN :  MV_NONE);
12433
12434   if (!IN_LEV_FIELD(new_jx, new_jy))
12435     return MP_NO_ACTION;
12436
12437   if (!player_can_move)
12438   {
12439     if (player->MovPos == 0)
12440     {
12441       player->is_moving = FALSE;
12442       player->is_digging = FALSE;
12443       player->is_collecting = FALSE;
12444       player->is_snapping = FALSE;
12445       player->is_pushing = FALSE;
12446     }
12447   }
12448
12449   if (!network.enabled && game.centered_player_nr == -1 &&
12450       !AllPlayersInSight(player, new_jx, new_jy))
12451     return MP_NO_ACTION;
12452
12453   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12454   if (can_move != MP_MOVING)
12455     return can_move;
12456
12457   // check if DigField() has caused relocation of the player
12458   if (player->jx != jx || player->jy != jy)
12459     return MP_NO_ACTION;        // <-- !!! CHECK THIS [-> MP_ACTION ?] !!!
12460
12461   StorePlayer[jx][jy] = 0;
12462   player->last_jx = jx;
12463   player->last_jy = jy;
12464   player->jx = new_jx;
12465   player->jy = new_jy;
12466   StorePlayer[new_jx][new_jy] = player->element_nr;
12467
12468   if (player->move_delay_value_next != -1)
12469   {
12470     player->move_delay_value = player->move_delay_value_next;
12471     player->move_delay_value_next = -1;
12472   }
12473
12474   player->MovPos =
12475     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12476
12477   player->step_counter++;
12478
12479   PlayerVisit[jx][jy] = FrameCounter;
12480
12481   player->is_moving = TRUE;
12482
12483 #if 1
12484   // should better be called in MovePlayer(), but this breaks some tapes
12485   ScrollPlayer(player, SCROLL_INIT);
12486 #endif
12487
12488   return MP_MOVING;
12489 }
12490
12491 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12492 {
12493   int jx = player->jx, jy = player->jy;
12494   int old_jx = jx, old_jy = jy;
12495   int moved = MP_NO_ACTION;
12496
12497   if (!player->active)
12498     return FALSE;
12499
12500   if (!dx && !dy)
12501   {
12502     if (player->MovPos == 0)
12503     {
12504       player->is_moving = FALSE;
12505       player->is_digging = FALSE;
12506       player->is_collecting = FALSE;
12507       player->is_snapping = FALSE;
12508       player->is_pushing = FALSE;
12509     }
12510
12511     return FALSE;
12512   }
12513
12514   if (player->move_delay > 0)
12515     return FALSE;
12516
12517   player->move_delay = -1;              // set to "uninitialized" value
12518
12519   // store if player is automatically moved to next field
12520   player->is_auto_moving = (player->programmed_action != MV_NONE);
12521
12522   // remove the last programmed player action
12523   player->programmed_action = 0;
12524
12525   if (player->MovPos)
12526   {
12527     // should only happen if pre-1.2 tape recordings are played
12528     // this is only for backward compatibility
12529
12530     int original_move_delay_value = player->move_delay_value;
12531
12532 #if DEBUG
12533     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
12534            tape.counter);
12535 #endif
12536
12537     // scroll remaining steps with finest movement resolution
12538     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12539
12540     while (player->MovPos)
12541     {
12542       ScrollPlayer(player, SCROLL_GO_ON);
12543       ScrollScreen(NULL, SCROLL_GO_ON);
12544
12545       AdvanceFrameAndPlayerCounters(player->index_nr);
12546
12547       DrawAllPlayers();
12548       BackToFront_WithFrameDelay(0);
12549     }
12550
12551     player->move_delay_value = original_move_delay_value;
12552   }
12553
12554   player->is_active = FALSE;
12555
12556   if (player->last_move_dir & MV_HORIZONTAL)
12557   {
12558     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12559       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12560   }
12561   else
12562   {
12563     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12564       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12565   }
12566
12567   if (!moved && !player->is_active)
12568   {
12569     player->is_moving = FALSE;
12570     player->is_digging = FALSE;
12571     player->is_collecting = FALSE;
12572     player->is_snapping = FALSE;
12573     player->is_pushing = FALSE;
12574   }
12575
12576   jx = player->jx;
12577   jy = player->jy;
12578
12579   if (moved & MP_MOVING && !ScreenMovPos &&
12580       (player->index_nr == game.centered_player_nr ||
12581        game.centered_player_nr == -1))
12582   {
12583     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12584
12585     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12586     {
12587       // actual player has left the screen -- scroll in that direction
12588       if (jx != old_jx)         // player has moved horizontally
12589         scroll_x += (jx - old_jx);
12590       else                      // player has moved vertically
12591         scroll_y += (jy - old_jy);
12592     }
12593     else
12594     {
12595       int offset_raw = game.scroll_delay_value;
12596
12597       if (jx != old_jx)         // player has moved horizontally
12598       {
12599         int offset = MIN(offset_raw, (SCR_FIELDX - 2) / 2);
12600         int offset_x = offset * (player->MovDir == MV_LEFT ? +1 : -1);
12601         int new_scroll_x = jx - MIDPOSX + offset_x;
12602
12603         if ((player->MovDir == MV_LEFT  && scroll_x > new_scroll_x) ||
12604             (player->MovDir == MV_RIGHT && scroll_x < new_scroll_x))
12605           scroll_x = new_scroll_x;
12606
12607         // don't scroll over playfield boundaries
12608         scroll_x = MIN(MAX(SBX_Left, scroll_x), SBX_Right);
12609
12610         // don't scroll more than one field at a time
12611         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12612
12613         // don't scroll against the player's moving direction
12614         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12615             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12616           scroll_x = old_scroll_x;
12617       }
12618       else                      // player has moved vertically
12619       {
12620         int offset = MIN(offset_raw, (SCR_FIELDY - 2) / 2);
12621         int offset_y = offset * (player->MovDir == MV_UP ? +1 : -1);
12622         int new_scroll_y = jy - MIDPOSY + offset_y;
12623
12624         if ((player->MovDir == MV_UP   && scroll_y > new_scroll_y) ||
12625             (player->MovDir == MV_DOWN && scroll_y < new_scroll_y))
12626           scroll_y = new_scroll_y;
12627
12628         // don't scroll over playfield boundaries
12629         scroll_y = MIN(MAX(SBY_Upper, scroll_y), SBY_Lower);
12630
12631         // don't scroll more than one field at a time
12632         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12633
12634         // don't scroll against the player's moving direction
12635         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12636             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12637           scroll_y = old_scroll_y;
12638       }
12639     }
12640
12641     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12642     {
12643       if (!network.enabled && game.centered_player_nr == -1 &&
12644           !AllPlayersInVisibleScreen())
12645       {
12646         scroll_x = old_scroll_x;
12647         scroll_y = old_scroll_y;
12648       }
12649       else
12650       {
12651         ScrollScreen(player, SCROLL_INIT);
12652         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12653       }
12654     }
12655   }
12656
12657   player->StepFrame = 0;
12658
12659   if (moved & MP_MOVING)
12660   {
12661     if (old_jx != jx && old_jy == jy)
12662       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12663     else if (old_jx == jx && old_jy != jy)
12664       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12665
12666     TEST_DrawLevelField(jx, jy);        // for "crumbled sand"
12667
12668     player->last_move_dir = player->MovDir;
12669     player->is_moving = TRUE;
12670     player->is_snapping = FALSE;
12671     player->is_switching = FALSE;
12672     player->is_dropping = FALSE;
12673     player->is_dropping_pressed = FALSE;
12674     player->drop_pressed_delay = 0;
12675
12676 #if 0
12677     // should better be called here than above, but this breaks some tapes
12678     ScrollPlayer(player, SCROLL_INIT);
12679 #endif
12680   }
12681   else
12682   {
12683     CheckGravityMovementWhenNotMoving(player);
12684
12685     player->is_moving = FALSE;
12686
12687     /* at this point, the player is allowed to move, but cannot move right now
12688        (e.g. because of something blocking the way) -- ensure that the player
12689        is also allowed to move in the next frame (in old versions before 3.1.1,
12690        the player was forced to wait again for eight frames before next try) */
12691
12692     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12693       player->move_delay = 0;   // allow direct movement in the next frame
12694   }
12695
12696   if (player->move_delay == -1)         // not yet initialized by DigField()
12697     player->move_delay = player->move_delay_value;
12698
12699   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12700   {
12701     TestIfPlayerTouchesBadThing(jx, jy);
12702     TestIfPlayerTouchesCustomElement(jx, jy);
12703   }
12704
12705   if (!player->active)
12706     RemovePlayer(player);
12707
12708   return moved;
12709 }
12710
12711 void ScrollPlayer(struct PlayerInfo *player, int mode)
12712 {
12713   int jx = player->jx, jy = player->jy;
12714   int last_jx = player->last_jx, last_jy = player->last_jy;
12715   int move_stepsize = TILEX / player->move_delay_value;
12716
12717   if (!player->active)
12718     return;
12719
12720   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      // player not moving
12721     return;
12722
12723   if (mode == SCROLL_INIT)
12724   {
12725     player->actual_frame_counter = FrameCounter;
12726     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12727
12728     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12729         Feld[last_jx][last_jy] == EL_EMPTY)
12730     {
12731       int last_field_block_delay = 0;   // start with no blocking at all
12732       int block_delay_adjustment = player->block_delay_adjustment;
12733
12734       // if player blocks last field, add delay for exactly one move
12735       if (player->block_last_field)
12736       {
12737         last_field_block_delay += player->move_delay_value;
12738
12739         // when blocking enabled, prevent moving up despite gravity
12740         if (player->gravity && player->MovDir == MV_UP)
12741           block_delay_adjustment = -1;
12742       }
12743
12744       // add block delay adjustment (also possible when not blocking)
12745       last_field_block_delay += block_delay_adjustment;
12746
12747       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12748       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12749     }
12750
12751     if (player->MovPos != 0)    // player has not yet reached destination
12752       return;
12753   }
12754   else if (!FrameReached(&player->actual_frame_counter, 1))
12755     return;
12756
12757   if (player->MovPos != 0)
12758   {
12759     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12760     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12761
12762     // before DrawPlayer() to draw correct player graphic for this case
12763     if (player->MovPos == 0)
12764       CheckGravityMovement(player);
12765   }
12766
12767   if (player->MovPos == 0)      // player reached destination field
12768   {
12769     if (player->move_delay_reset_counter > 0)
12770     {
12771       player->move_delay_reset_counter--;
12772
12773       if (player->move_delay_reset_counter == 0)
12774       {
12775         // continue with normal speed after quickly moving through gate
12776         HALVE_PLAYER_SPEED(player);
12777
12778         // be able to make the next move without delay
12779         player->move_delay = 0;
12780       }
12781     }
12782
12783     player->last_jx = jx;
12784     player->last_jy = jy;
12785
12786     if (Feld[jx][jy] == EL_EXIT_OPEN ||
12787         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12788         Feld[jx][jy] == EL_EM_EXIT_OPENING ||
12789         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12790         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12791         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12792         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12793         Feld[jx][jy] == EL_SP_EXIT_OPENING)     // <-- special case
12794     {
12795       ExitPlayer(player);
12796
12797       if (game.players_still_needed == 0 &&
12798           (game.friends_still_needed == 0 ||
12799            IS_SP_ELEMENT(Feld[jx][jy])))
12800         LevelSolved();
12801     }
12802
12803     // this breaks one level: "machine", level 000
12804     {
12805       int move_direction = player->MovDir;
12806       int enter_side = MV_DIR_OPPOSITE(move_direction);
12807       int leave_side = move_direction;
12808       int old_jx = last_jx;
12809       int old_jy = last_jy;
12810       int old_element = Feld[old_jx][old_jy];
12811       int new_element = Feld[jx][jy];
12812
12813       if (IS_CUSTOM_ELEMENT(old_element))
12814         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12815                                    CE_LEFT_BY_PLAYER,
12816                                    player->index_bit, leave_side);
12817
12818       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12819                                           CE_PLAYER_LEAVES_X,
12820                                           player->index_bit, leave_side);
12821
12822       if (IS_CUSTOM_ELEMENT(new_element))
12823         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12824                                    player->index_bit, enter_side);
12825
12826       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12827                                           CE_PLAYER_ENTERS_X,
12828                                           player->index_bit, enter_side);
12829
12830       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12831                                         CE_MOVE_OF_X, move_direction);
12832     }
12833
12834     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12835     {
12836       TestIfPlayerTouchesBadThing(jx, jy);
12837       TestIfPlayerTouchesCustomElement(jx, jy);
12838
12839       /* needed because pushed element has not yet reached its destination,
12840          so it would trigger a change event at its previous field location */
12841       if (!player->is_pushing)
12842         TestIfElementTouchesCustomElement(jx, jy);      // for empty space
12843
12844       if (!player->active)
12845         RemovePlayer(player);
12846     }
12847
12848     if (!game.LevelSolved && level.use_step_counter)
12849     {
12850       int i;
12851
12852       TimePlayed++;
12853
12854       if (TimeLeft > 0)
12855       {
12856         TimeLeft--;
12857
12858         if (TimeLeft <= 10 && setup.time_limit)
12859           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12860
12861         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12862
12863         DisplayGameControlValues();
12864
12865         if (!TimeLeft && setup.time_limit)
12866           for (i = 0; i < MAX_PLAYERS; i++)
12867             KillPlayer(&stored_player[i]);
12868       }
12869       else if (game.no_time_limit && !game.all_players_gone)
12870       {
12871         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12872
12873         DisplayGameControlValues();
12874       }
12875     }
12876
12877     if (tape.single_step && tape.recording && !tape.pausing &&
12878         !player->programmed_action)
12879       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12880
12881     if (!player->programmed_action)
12882       CheckSaveEngineSnapshot(player);
12883   }
12884 }
12885
12886 void ScrollScreen(struct PlayerInfo *player, int mode)
12887 {
12888   static unsigned int screen_frame_counter = 0;
12889
12890   if (mode == SCROLL_INIT)
12891   {
12892     // set scrolling step size according to actual player's moving speed
12893     ScrollStepSize = TILEX / player->move_delay_value;
12894
12895     screen_frame_counter = FrameCounter;
12896     ScreenMovDir = player->MovDir;
12897     ScreenMovPos = player->MovPos;
12898     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12899     return;
12900   }
12901   else if (!FrameReached(&screen_frame_counter, 1))
12902     return;
12903
12904   if (ScreenMovPos)
12905   {
12906     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12907     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12908     redraw_mask |= REDRAW_FIELD;
12909   }
12910   else
12911     ScreenMovDir = MV_NONE;
12912 }
12913
12914 void TestIfPlayerTouchesCustomElement(int x, int y)
12915 {
12916   static int xy[4][2] =
12917   {
12918     { 0, -1 },
12919     { -1, 0 },
12920     { +1, 0 },
12921     { 0, +1 }
12922   };
12923   static int trigger_sides[4][2] =
12924   {
12925     // center side       border side
12926     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
12927     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
12928     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
12929     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
12930   };
12931   static int touch_dir[4] =
12932   {
12933     MV_LEFT | MV_RIGHT,
12934     MV_UP   | MV_DOWN,
12935     MV_UP   | MV_DOWN,
12936     MV_LEFT | MV_RIGHT
12937   };
12938   int center_element = Feld[x][y];      // should always be non-moving!
12939   int i;
12940
12941   for (i = 0; i < NUM_DIRECTIONS; i++)
12942   {
12943     int xx = x + xy[i][0];
12944     int yy = y + xy[i][1];
12945     int center_side = trigger_sides[i][0];
12946     int border_side = trigger_sides[i][1];
12947     int border_element;
12948
12949     if (!IN_LEV_FIELD(xx, yy))
12950       continue;
12951
12952     if (IS_PLAYER(x, y))                // player found at center element
12953     {
12954       struct PlayerInfo *player = PLAYERINFO(x, y);
12955
12956       if (game.engine_version < VERSION_IDENT(3,0,7,0))
12957         border_element = Feld[xx][yy];          // may be moving!
12958       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12959         border_element = Feld[xx][yy];
12960       else if (MovDir[xx][yy] & touch_dir[i])   // elements are touching
12961         border_element = MovingOrBlocked2Element(xx, yy);
12962       else
12963         continue;               // center and border element do not touch
12964
12965       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12966                                  player->index_bit, border_side);
12967       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12968                                           CE_PLAYER_TOUCHES_X,
12969                                           player->index_bit, border_side);
12970
12971       {
12972         /* use player element that is initially defined in the level playfield,
12973            not the player element that corresponds to the runtime player number
12974            (example: a level that contains EL_PLAYER_3 as the only player would
12975            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12976         int player_element = PLAYERINFO(x, y)->initial_element;
12977
12978         CheckElementChangeBySide(xx, yy, border_element, player_element,
12979                                  CE_TOUCHING_X, border_side);
12980       }
12981     }
12982     else if (IS_PLAYER(xx, yy))         // player found at border element
12983     {
12984       struct PlayerInfo *player = PLAYERINFO(xx, yy);
12985
12986       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12987       {
12988         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12989           continue;             // center and border element do not touch
12990       }
12991
12992       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12993                                  player->index_bit, center_side);
12994       CheckTriggeredElementChangeByPlayer(x, y, center_element,
12995                                           CE_PLAYER_TOUCHES_X,
12996                                           player->index_bit, center_side);
12997
12998       {
12999         /* use player element that is initially defined in the level playfield,
13000            not the player element that corresponds to the runtime player number
13001            (example: a level that contains EL_PLAYER_3 as the only player would
13002            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13003         int player_element = PLAYERINFO(xx, yy)->initial_element;
13004
13005         CheckElementChangeBySide(x, y, center_element, player_element,
13006                                  CE_TOUCHING_X, center_side);
13007       }
13008
13009       break;
13010     }
13011   }
13012 }
13013
13014 void TestIfElementTouchesCustomElement(int x, int y)
13015 {
13016   static int xy[4][2] =
13017   {
13018     { 0, -1 },
13019     { -1, 0 },
13020     { +1, 0 },
13021     { 0, +1 }
13022   };
13023   static int trigger_sides[4][2] =
13024   {
13025     // center side      border side
13026     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13027     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13028     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13029     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13030   };
13031   static int touch_dir[4] =
13032   {
13033     MV_LEFT | MV_RIGHT,
13034     MV_UP   | MV_DOWN,
13035     MV_UP   | MV_DOWN,
13036     MV_LEFT | MV_RIGHT
13037   };
13038   boolean change_center_element = FALSE;
13039   int center_element = Feld[x][y];      // should always be non-moving!
13040   int border_element_old[NUM_DIRECTIONS];
13041   int i;
13042
13043   for (i = 0; i < NUM_DIRECTIONS; i++)
13044   {
13045     int xx = x + xy[i][0];
13046     int yy = y + xy[i][1];
13047     int border_element;
13048
13049     border_element_old[i] = -1;
13050
13051     if (!IN_LEV_FIELD(xx, yy))
13052       continue;
13053
13054     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13055       border_element = Feld[xx][yy];    // may be moving!
13056     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13057       border_element = Feld[xx][yy];
13058     else if (MovDir[xx][yy] & touch_dir[i])     // elements are touching
13059       border_element = MovingOrBlocked2Element(xx, yy);
13060     else
13061       continue;                 // center and border element do not touch
13062
13063     border_element_old[i] = border_element;
13064   }
13065
13066   for (i = 0; i < NUM_DIRECTIONS; i++)
13067   {
13068     int xx = x + xy[i][0];
13069     int yy = y + xy[i][1];
13070     int center_side = trigger_sides[i][0];
13071     int border_element = border_element_old[i];
13072
13073     if (border_element == -1)
13074       continue;
13075
13076     // check for change of border element
13077     CheckElementChangeBySide(xx, yy, border_element, center_element,
13078                              CE_TOUCHING_X, center_side);
13079
13080     // (center element cannot be player, so we dont have to check this here)
13081   }
13082
13083   for (i = 0; i < NUM_DIRECTIONS; i++)
13084   {
13085     int xx = x + xy[i][0];
13086     int yy = y + xy[i][1];
13087     int border_side = trigger_sides[i][1];
13088     int border_element = border_element_old[i];
13089
13090     if (border_element == -1)
13091       continue;
13092
13093     // check for change of center element (but change it only once)
13094     if (!change_center_element)
13095       change_center_element =
13096         CheckElementChangeBySide(x, y, center_element, border_element,
13097                                  CE_TOUCHING_X, border_side);
13098
13099     if (IS_PLAYER(xx, yy))
13100     {
13101       /* use player element that is initially defined in the level playfield,
13102          not the player element that corresponds to the runtime player number
13103          (example: a level that contains EL_PLAYER_3 as the only player would
13104          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13105       int player_element = PLAYERINFO(xx, yy)->initial_element;
13106
13107       CheckElementChangeBySide(x, y, center_element, player_element,
13108                                CE_TOUCHING_X, border_side);
13109     }
13110   }
13111 }
13112
13113 void TestIfElementHitsCustomElement(int x, int y, int direction)
13114 {
13115   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13116   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13117   int hitx = x + dx, hity = y + dy;
13118   int hitting_element = Feld[x][y];
13119   int touched_element;
13120
13121   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13122     return;
13123
13124   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13125                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13126
13127   if (IN_LEV_FIELD(hitx, hity))
13128   {
13129     int opposite_direction = MV_DIR_OPPOSITE(direction);
13130     int hitting_side = direction;
13131     int touched_side = opposite_direction;
13132     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13133                           MovDir[hitx][hity] != direction ||
13134                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13135
13136     object_hit = TRUE;
13137
13138     if (object_hit)
13139     {
13140       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13141                                CE_HITTING_X, touched_side);
13142
13143       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13144                                CE_HIT_BY_X, hitting_side);
13145
13146       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13147                                CE_HIT_BY_SOMETHING, opposite_direction);
13148
13149       if (IS_PLAYER(hitx, hity))
13150       {
13151         /* use player element that is initially defined in the level playfield,
13152            not the player element that corresponds to the runtime player number
13153            (example: a level that contains EL_PLAYER_3 as the only player would
13154            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13155         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13156
13157         CheckElementChangeBySide(x, y, hitting_element, player_element,
13158                                  CE_HITTING_X, touched_side);
13159       }
13160     }
13161   }
13162
13163   // "hitting something" is also true when hitting the playfield border
13164   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13165                            CE_HITTING_SOMETHING, direction);
13166 }
13167
13168 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13169 {
13170   int i, kill_x = -1, kill_y = -1;
13171
13172   int bad_element = -1;
13173   static int test_xy[4][2] =
13174   {
13175     { 0, -1 },
13176     { -1, 0 },
13177     { +1, 0 },
13178     { 0, +1 }
13179   };
13180   static int test_dir[4] =
13181   {
13182     MV_UP,
13183     MV_LEFT,
13184     MV_RIGHT,
13185     MV_DOWN
13186   };
13187
13188   for (i = 0; i < NUM_DIRECTIONS; i++)
13189   {
13190     int test_x, test_y, test_move_dir, test_element;
13191
13192     test_x = good_x + test_xy[i][0];
13193     test_y = good_y + test_xy[i][1];
13194
13195     if (!IN_LEV_FIELD(test_x, test_y))
13196       continue;
13197
13198     test_move_dir =
13199       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13200
13201     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13202
13203     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13204        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13205     */
13206     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13207         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13208     {
13209       kill_x = test_x;
13210       kill_y = test_y;
13211       bad_element = test_element;
13212
13213       break;
13214     }
13215   }
13216
13217   if (kill_x != -1 || kill_y != -1)
13218   {
13219     if (IS_PLAYER(good_x, good_y))
13220     {
13221       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13222
13223       if (player->shield_deadly_time_left > 0 &&
13224           !IS_INDESTRUCTIBLE(bad_element))
13225         Bang(kill_x, kill_y);
13226       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13227         KillPlayer(player);
13228     }
13229     else
13230       Bang(good_x, good_y);
13231   }
13232 }
13233
13234 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13235 {
13236   int i, kill_x = -1, kill_y = -1;
13237   int bad_element = Feld[bad_x][bad_y];
13238   static int test_xy[4][2] =
13239   {
13240     { 0, -1 },
13241     { -1, 0 },
13242     { +1, 0 },
13243     { 0, +1 }
13244   };
13245   static int touch_dir[4] =
13246   {
13247     MV_LEFT | MV_RIGHT,
13248     MV_UP   | MV_DOWN,
13249     MV_UP   | MV_DOWN,
13250     MV_LEFT | MV_RIGHT
13251   };
13252   static int test_dir[4] =
13253   {
13254     MV_UP,
13255     MV_LEFT,
13256     MV_RIGHT,
13257     MV_DOWN
13258   };
13259
13260   if (bad_element == EL_EXPLOSION)      // skip just exploding bad things
13261     return;
13262
13263   for (i = 0; i < NUM_DIRECTIONS; i++)
13264   {
13265     int test_x, test_y, test_move_dir, test_element;
13266
13267     test_x = bad_x + test_xy[i][0];
13268     test_y = bad_y + test_xy[i][1];
13269
13270     if (!IN_LEV_FIELD(test_x, test_y))
13271       continue;
13272
13273     test_move_dir =
13274       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13275
13276     test_element = Feld[test_x][test_y];
13277
13278     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13279        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13280     */
13281     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13282         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13283     {
13284       // good thing is player or penguin that does not move away
13285       if (IS_PLAYER(test_x, test_y))
13286       {
13287         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13288
13289         if (bad_element == EL_ROBOT && player->is_moving)
13290           continue;     // robot does not kill player if he is moving
13291
13292         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13293         {
13294           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13295             continue;           // center and border element do not touch
13296         }
13297
13298         kill_x = test_x;
13299         kill_y = test_y;
13300
13301         break;
13302       }
13303       else if (test_element == EL_PENGUIN)
13304       {
13305         kill_x = test_x;
13306         kill_y = test_y;
13307
13308         break;
13309       }
13310     }
13311   }
13312
13313   if (kill_x != -1 || kill_y != -1)
13314   {
13315     if (IS_PLAYER(kill_x, kill_y))
13316     {
13317       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13318
13319       if (player->shield_deadly_time_left > 0 &&
13320           !IS_INDESTRUCTIBLE(bad_element))
13321         Bang(bad_x, bad_y);
13322       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13323         KillPlayer(player);
13324     }
13325     else
13326       Bang(kill_x, kill_y);
13327   }
13328 }
13329
13330 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13331 {
13332   int bad_element = Feld[bad_x][bad_y];
13333   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13334   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13335   int test_x = bad_x + dx, test_y = bad_y + dy;
13336   int test_move_dir, test_element;
13337   int kill_x = -1, kill_y = -1;
13338
13339   if (!IN_LEV_FIELD(test_x, test_y))
13340     return;
13341
13342   test_move_dir =
13343     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13344
13345   test_element = Feld[test_x][test_y];
13346
13347   if (test_move_dir != bad_move_dir)
13348   {
13349     // good thing can be player or penguin that does not move away
13350     if (IS_PLAYER(test_x, test_y))
13351     {
13352       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13353
13354       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13355          player as being hit when he is moving towards the bad thing, because
13356          the "get hit by" condition would be lost after the player stops) */
13357       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13358         return;         // player moves away from bad thing
13359
13360       kill_x = test_x;
13361       kill_y = test_y;
13362     }
13363     else if (test_element == EL_PENGUIN)
13364     {
13365       kill_x = test_x;
13366       kill_y = test_y;
13367     }
13368   }
13369
13370   if (kill_x != -1 || kill_y != -1)
13371   {
13372     if (IS_PLAYER(kill_x, kill_y))
13373     {
13374       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13375
13376       if (player->shield_deadly_time_left > 0 &&
13377           !IS_INDESTRUCTIBLE(bad_element))
13378         Bang(bad_x, bad_y);
13379       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13380         KillPlayer(player);
13381     }
13382     else
13383       Bang(kill_x, kill_y);
13384   }
13385 }
13386
13387 void TestIfPlayerTouchesBadThing(int x, int y)
13388 {
13389   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13390 }
13391
13392 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13393 {
13394   TestIfGoodThingHitsBadThing(x, y, move_dir);
13395 }
13396
13397 void TestIfBadThingTouchesPlayer(int x, int y)
13398 {
13399   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13400 }
13401
13402 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13403 {
13404   TestIfBadThingHitsGoodThing(x, y, move_dir);
13405 }
13406
13407 void TestIfFriendTouchesBadThing(int x, int y)
13408 {
13409   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13410 }
13411
13412 void TestIfBadThingTouchesFriend(int x, int y)
13413 {
13414   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13415 }
13416
13417 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13418 {
13419   int i, kill_x = bad_x, kill_y = bad_y;
13420   static int xy[4][2] =
13421   {
13422     { 0, -1 },
13423     { -1, 0 },
13424     { +1, 0 },
13425     { 0, +1 }
13426   };
13427
13428   for (i = 0; i < NUM_DIRECTIONS; i++)
13429   {
13430     int x, y, element;
13431
13432     x = bad_x + xy[i][0];
13433     y = bad_y + xy[i][1];
13434     if (!IN_LEV_FIELD(x, y))
13435       continue;
13436
13437     element = Feld[x][y];
13438     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13439         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13440     {
13441       kill_x = x;
13442       kill_y = y;
13443       break;
13444     }
13445   }
13446
13447   if (kill_x != bad_x || kill_y != bad_y)
13448     Bang(bad_x, bad_y);
13449 }
13450
13451 void KillPlayer(struct PlayerInfo *player)
13452 {
13453   int jx = player->jx, jy = player->jy;
13454
13455   if (!player->active)
13456     return;
13457
13458 #if 0
13459   printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
13460          player->killed, player->active, player->reanimated);
13461 #endif
13462
13463   /* the following code was introduced to prevent an infinite loop when calling
13464      -> Bang()
13465      -> CheckTriggeredElementChangeExt()
13466      -> ExecuteCustomElementAction()
13467      -> KillPlayer()
13468      -> (infinitely repeating the above sequence of function calls)
13469      which occurs when killing the player while having a CE with the setting
13470      "kill player X when explosion of <player X>"; the solution using a new
13471      field "player->killed" was chosen for backwards compatibility, although
13472      clever use of the fields "player->active" etc. would probably also work */
13473 #if 1
13474   if (player->killed)
13475     return;
13476 #endif
13477
13478   player->killed = TRUE;
13479
13480   // remove accessible field at the player's position
13481   Feld[jx][jy] = EL_EMPTY;
13482
13483   // deactivate shield (else Bang()/Explode() would not work right)
13484   player->shield_normal_time_left = 0;
13485   player->shield_deadly_time_left = 0;
13486
13487 #if 0
13488   printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
13489          player->killed, player->active, player->reanimated);
13490 #endif
13491
13492   Bang(jx, jy);
13493
13494 #if 0
13495   printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
13496          player->killed, player->active, player->reanimated);
13497 #endif
13498
13499   if (player->reanimated)       // killed player may have been reanimated
13500     player->killed = player->reanimated = FALSE;
13501   else
13502     BuryPlayer(player);
13503 }
13504
13505 static void KillPlayerUnlessEnemyProtected(int x, int y)
13506 {
13507   if (!PLAYER_ENEMY_PROTECTED(x, y))
13508     KillPlayer(PLAYERINFO(x, y));
13509 }
13510
13511 static void KillPlayerUnlessExplosionProtected(int x, int y)
13512 {
13513   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13514     KillPlayer(PLAYERINFO(x, y));
13515 }
13516
13517 void BuryPlayer(struct PlayerInfo *player)
13518 {
13519   int jx = player->jx, jy = player->jy;
13520
13521   if (!player->active)
13522     return;
13523
13524   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13525   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13526
13527   RemovePlayer(player);
13528
13529   player->buried = TRUE;
13530
13531   if (game.all_players_gone)
13532     game.GameOver = TRUE;
13533 }
13534
13535 void RemovePlayer(struct PlayerInfo *player)
13536 {
13537   int jx = player->jx, jy = player->jy;
13538   int i, found = FALSE;
13539
13540   player->present = FALSE;
13541   player->active = FALSE;
13542
13543   // required for some CE actions (even if the player is not active anymore)
13544   player->MovPos = 0;
13545
13546   if (!ExplodeField[jx][jy])
13547     StorePlayer[jx][jy] = 0;
13548
13549   if (player->is_moving)
13550     TEST_DrawLevelField(player->last_jx, player->last_jy);
13551
13552   for (i = 0; i < MAX_PLAYERS; i++)
13553     if (stored_player[i].active)
13554       found = TRUE;
13555
13556   if (!found)
13557   {
13558     game.all_players_gone = TRUE;
13559     game.GameOver = TRUE;
13560   }
13561
13562   game.exit_x = game.robot_wheel_x = jx;
13563   game.exit_y = game.robot_wheel_y = jy;
13564 }
13565
13566 void ExitPlayer(struct PlayerInfo *player)
13567 {
13568   DrawPlayer(player);   // needed here only to cleanup last field
13569   RemovePlayer(player);
13570
13571   if (game.players_still_needed > 0)
13572     game.players_still_needed--;
13573 }
13574
13575 static void setFieldForSnapping(int x, int y, int element, int direction)
13576 {
13577   struct ElementInfo *ei = &element_info[element];
13578   int direction_bit = MV_DIR_TO_BIT(direction);
13579   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13580   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13581                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13582
13583   Feld[x][y] = EL_ELEMENT_SNAPPING;
13584   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13585
13586   ResetGfxAnimation(x, y);
13587
13588   GfxElement[x][y] = element;
13589   GfxAction[x][y] = action;
13590   GfxDir[x][y] = direction;
13591   GfxFrame[x][y] = -1;
13592 }
13593
13594 /*
13595   =============================================================================
13596   checkDiagonalPushing()
13597   -----------------------------------------------------------------------------
13598   check if diagonal input device direction results in pushing of object
13599   (by checking if the alternative direction is walkable, diggable, ...)
13600   =============================================================================
13601 */
13602
13603 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13604                                     int x, int y, int real_dx, int real_dy)
13605 {
13606   int jx, jy, dx, dy, xx, yy;
13607
13608   if (real_dx == 0 || real_dy == 0)     // no diagonal direction => push
13609     return TRUE;
13610
13611   // diagonal direction: check alternative direction
13612   jx = player->jx;
13613   jy = player->jy;
13614   dx = x - jx;
13615   dy = y - jy;
13616   xx = jx + (dx == 0 ? real_dx : 0);
13617   yy = jy + (dy == 0 ? real_dy : 0);
13618
13619   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13620 }
13621
13622 /*
13623   =============================================================================
13624   DigField()
13625   -----------------------------------------------------------------------------
13626   x, y:                 field next to player (non-diagonal) to try to dig to
13627   real_dx, real_dy:     direction as read from input device (can be diagonal)
13628   =============================================================================
13629 */
13630
13631 static int DigField(struct PlayerInfo *player,
13632                     int oldx, int oldy, int x, int y,
13633                     int real_dx, int real_dy, int mode)
13634 {
13635   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13636   boolean player_was_pushing = player->is_pushing;
13637   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13638   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13639   int jx = oldx, jy = oldy;
13640   int dx = x - jx, dy = y - jy;
13641   int nextx = x + dx, nexty = y + dy;
13642   int move_direction = (dx == -1 ? MV_LEFT  :
13643                         dx == +1 ? MV_RIGHT :
13644                         dy == -1 ? MV_UP    :
13645                         dy == +1 ? MV_DOWN  : MV_NONE);
13646   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13647   int dig_side = MV_DIR_OPPOSITE(move_direction);
13648   int old_element = Feld[jx][jy];
13649   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13650   int collect_count;
13651
13652   if (is_player)                // function can also be called by EL_PENGUIN
13653   {
13654     if (player->MovPos == 0)
13655     {
13656       player->is_digging = FALSE;
13657       player->is_collecting = FALSE;
13658     }
13659
13660     if (player->MovPos == 0)    // last pushing move finished
13661       player->is_pushing = FALSE;
13662
13663     if (mode == DF_NO_PUSH)     // player just stopped pushing
13664     {
13665       player->is_switching = FALSE;
13666       player->push_delay = -1;
13667
13668       return MP_NO_ACTION;
13669     }
13670   }
13671
13672   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13673     old_element = Back[jx][jy];
13674
13675   // in case of element dropped at player position, check background
13676   else if (Back[jx][jy] != EL_EMPTY &&
13677            game.engine_version >= VERSION_IDENT(2,2,0,0))
13678     old_element = Back[jx][jy];
13679
13680   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13681     return MP_NO_ACTION;        // field has no opening in this direction
13682
13683   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13684     return MP_NO_ACTION;        // field has no opening in this direction
13685
13686   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13687   {
13688     SplashAcid(x, y);
13689
13690     Feld[jx][jy] = player->artwork_element;
13691     InitMovingField(jx, jy, MV_DOWN);
13692     Store[jx][jy] = EL_ACID;
13693     ContinueMoving(jx, jy);
13694     BuryPlayer(player);
13695
13696     return MP_DONT_RUN_INTO;
13697   }
13698
13699   if (player_can_move && DONT_RUN_INTO(element))
13700   {
13701     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13702
13703     return MP_DONT_RUN_INTO;
13704   }
13705
13706   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13707     return MP_NO_ACTION;
13708
13709   collect_count = element_info[element].collect_count_initial;
13710
13711   if (!is_player && !IS_COLLECTIBLE(element))   // penguin cannot collect it
13712     return MP_NO_ACTION;
13713
13714   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13715     player_can_move = player_can_move_or_snap;
13716
13717   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13718       game.engine_version >= VERSION_IDENT(2,2,0,0))
13719   {
13720     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13721                                player->index_bit, dig_side);
13722     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13723                                         player->index_bit, dig_side);
13724
13725     if (element == EL_DC_LANDMINE)
13726       Bang(x, y);
13727
13728     if (Feld[x][y] != element)          // field changed by snapping
13729       return MP_ACTION;
13730
13731     return MP_NO_ACTION;
13732   }
13733
13734   if (player->gravity && is_player && !player->is_auto_moving &&
13735       canFallDown(player) && move_direction != MV_DOWN &&
13736       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13737     return MP_NO_ACTION;        // player cannot walk here due to gravity
13738
13739   if (player_can_move &&
13740       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13741   {
13742     int sound_element = SND_ELEMENT(element);
13743     int sound_action = ACTION_WALKING;
13744
13745     if (IS_RND_GATE(element))
13746     {
13747       if (!player->key[RND_GATE_NR(element)])
13748         return MP_NO_ACTION;
13749     }
13750     else if (IS_RND_GATE_GRAY(element))
13751     {
13752       if (!player->key[RND_GATE_GRAY_NR(element)])
13753         return MP_NO_ACTION;
13754     }
13755     else if (IS_RND_GATE_GRAY_ACTIVE(element))
13756     {
13757       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13758         return MP_NO_ACTION;
13759     }
13760     else if (element == EL_EXIT_OPEN ||
13761              element == EL_EM_EXIT_OPEN ||
13762              element == EL_EM_EXIT_OPENING ||
13763              element == EL_STEEL_EXIT_OPEN ||
13764              element == EL_EM_STEEL_EXIT_OPEN ||
13765              element == EL_EM_STEEL_EXIT_OPENING ||
13766              element == EL_SP_EXIT_OPEN ||
13767              element == EL_SP_EXIT_OPENING)
13768     {
13769       sound_action = ACTION_PASSING;    // player is passing exit
13770     }
13771     else if (element == EL_EMPTY)
13772     {
13773       sound_action = ACTION_MOVING;             // nothing to walk on
13774     }
13775
13776     // play sound from background or player, whatever is available
13777     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13778       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13779     else
13780       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13781   }
13782   else if (player_can_move &&
13783            IS_PASSABLE(element) && canPassField(x, y, move_direction))
13784   {
13785     if (!ACCESS_FROM(element, opposite_direction))
13786       return MP_NO_ACTION;      // field not accessible from this direction
13787
13788     if (CAN_MOVE(element))      // only fixed elements can be passed!
13789       return MP_NO_ACTION;
13790
13791     if (IS_EM_GATE(element))
13792     {
13793       if (!player->key[EM_GATE_NR(element)])
13794         return MP_NO_ACTION;
13795     }
13796     else if (IS_EM_GATE_GRAY(element))
13797     {
13798       if (!player->key[EM_GATE_GRAY_NR(element)])
13799         return MP_NO_ACTION;
13800     }
13801     else if (IS_EM_GATE_GRAY_ACTIVE(element))
13802     {
13803       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13804         return MP_NO_ACTION;
13805     }
13806     else if (IS_EMC_GATE(element))
13807     {
13808       if (!player->key[EMC_GATE_NR(element)])
13809         return MP_NO_ACTION;
13810     }
13811     else if (IS_EMC_GATE_GRAY(element))
13812     {
13813       if (!player->key[EMC_GATE_GRAY_NR(element)])
13814         return MP_NO_ACTION;
13815     }
13816     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13817     {
13818       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13819         return MP_NO_ACTION;
13820     }
13821     else if (element == EL_DC_GATE_WHITE ||
13822              element == EL_DC_GATE_WHITE_GRAY ||
13823              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13824     {
13825       if (player->num_white_keys == 0)
13826         return MP_NO_ACTION;
13827
13828       player->num_white_keys--;
13829     }
13830     else if (IS_SP_PORT(element))
13831     {
13832       if (element == EL_SP_GRAVITY_PORT_LEFT ||
13833           element == EL_SP_GRAVITY_PORT_RIGHT ||
13834           element == EL_SP_GRAVITY_PORT_UP ||
13835           element == EL_SP_GRAVITY_PORT_DOWN)
13836         player->gravity = !player->gravity;
13837       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13838                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13839                element == EL_SP_GRAVITY_ON_PORT_UP ||
13840                element == EL_SP_GRAVITY_ON_PORT_DOWN)
13841         player->gravity = TRUE;
13842       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13843                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13844                element == EL_SP_GRAVITY_OFF_PORT_UP ||
13845                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13846         player->gravity = FALSE;
13847     }
13848
13849     // automatically move to the next field with double speed
13850     player->programmed_action = move_direction;
13851
13852     if (player->move_delay_reset_counter == 0)
13853     {
13854       player->move_delay_reset_counter = 2;     // two double speed steps
13855
13856       DOUBLE_PLAYER_SPEED(player);
13857     }
13858
13859     PlayLevelSoundAction(x, y, ACTION_PASSING);
13860   }
13861   else if (player_can_move_or_snap && IS_DIGGABLE(element))
13862   {
13863     RemoveField(x, y);
13864
13865     if (mode != DF_SNAP)
13866     {
13867       GfxElement[x][y] = GFX_ELEMENT(element);
13868       player->is_digging = TRUE;
13869     }
13870
13871     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13872
13873     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13874                                         player->index_bit, dig_side);
13875
13876     if (mode == DF_SNAP)
13877     {
13878       if (level.block_snap_field)
13879         setFieldForSnapping(x, y, element, move_direction);
13880       else
13881         TestIfElementTouchesCustomElement(x, y);        // for empty space
13882
13883       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13884                                           player->index_bit, dig_side);
13885     }
13886   }
13887   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13888   {
13889     RemoveField(x, y);
13890
13891     if (is_player && mode != DF_SNAP)
13892     {
13893       GfxElement[x][y] = element;
13894       player->is_collecting = TRUE;
13895     }
13896
13897     if (element == EL_SPEED_PILL)
13898     {
13899       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13900     }
13901     else if (element == EL_EXTRA_TIME && level.time > 0)
13902     {
13903       TimeLeft += level.extra_time;
13904
13905       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13906
13907       DisplayGameControlValues();
13908     }
13909     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13910     {
13911       player->shield_normal_time_left += level.shield_normal_time;
13912       if (element == EL_SHIELD_DEADLY)
13913         player->shield_deadly_time_left += level.shield_deadly_time;
13914     }
13915     else if (element == EL_DYNAMITE ||
13916              element == EL_EM_DYNAMITE ||
13917              element == EL_SP_DISK_RED)
13918     {
13919       if (player->inventory_size < MAX_INVENTORY_SIZE)
13920         player->inventory_element[player->inventory_size++] = element;
13921
13922       DrawGameDoorValues();
13923     }
13924     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13925     {
13926       player->dynabomb_count++;
13927       player->dynabombs_left++;
13928     }
13929     else if (element == EL_DYNABOMB_INCREASE_SIZE)
13930     {
13931       player->dynabomb_size++;
13932     }
13933     else if (element == EL_DYNABOMB_INCREASE_POWER)
13934     {
13935       player->dynabomb_xl = TRUE;
13936     }
13937     else if (IS_KEY(element))
13938     {
13939       player->key[KEY_NR(element)] = TRUE;
13940
13941       DrawGameDoorValues();
13942     }
13943     else if (element == EL_DC_KEY_WHITE)
13944     {
13945       player->num_white_keys++;
13946
13947       // display white keys?
13948       // DrawGameDoorValues();
13949     }
13950     else if (IS_ENVELOPE(element))
13951     {
13952       player->show_envelope = element;
13953     }
13954     else if (element == EL_EMC_LENSES)
13955     {
13956       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13957
13958       RedrawAllInvisibleElementsForLenses();
13959     }
13960     else if (element == EL_EMC_MAGNIFIER)
13961     {
13962       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13963
13964       RedrawAllInvisibleElementsForMagnifier();
13965     }
13966     else if (IS_DROPPABLE(element) ||
13967              IS_THROWABLE(element))     // can be collected and dropped
13968     {
13969       int i;
13970
13971       if (collect_count == 0)
13972         player->inventory_infinite_element = element;
13973       else
13974         for (i = 0; i < collect_count; i++)
13975           if (player->inventory_size < MAX_INVENTORY_SIZE)
13976             player->inventory_element[player->inventory_size++] = element;
13977
13978       DrawGameDoorValues();
13979     }
13980     else if (collect_count > 0)
13981     {
13982       game.gems_still_needed -= collect_count;
13983       if (game.gems_still_needed < 0)
13984         game.gems_still_needed = 0;
13985
13986       game.snapshot.collected_item = TRUE;
13987
13988       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
13989
13990       DisplayGameControlValues();
13991     }
13992
13993     RaiseScoreElement(element);
13994     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13995
13996     if (is_player)
13997       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13998                                           player->index_bit, dig_side);
13999
14000     if (mode == DF_SNAP)
14001     {
14002       if (level.block_snap_field)
14003         setFieldForSnapping(x, y, element, move_direction);
14004       else
14005         TestIfElementTouchesCustomElement(x, y);        // for empty space
14006
14007       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14008                                           player->index_bit, dig_side);
14009     }
14010   }
14011   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14012   {
14013     if (mode == DF_SNAP && element != EL_BD_ROCK)
14014       return MP_NO_ACTION;
14015
14016     if (CAN_FALL(element) && dy)
14017       return MP_NO_ACTION;
14018
14019     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14020         !(element == EL_SPRING && level.use_spring_bug))
14021       return MP_NO_ACTION;
14022
14023     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14024         ((move_direction & MV_VERTICAL &&
14025           ((element_info[element].move_pattern & MV_LEFT &&
14026             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14027            (element_info[element].move_pattern & MV_RIGHT &&
14028             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14029          (move_direction & MV_HORIZONTAL &&
14030           ((element_info[element].move_pattern & MV_UP &&
14031             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14032            (element_info[element].move_pattern & MV_DOWN &&
14033             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14034       return MP_NO_ACTION;
14035
14036     // do not push elements already moving away faster than player
14037     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14038         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14039       return MP_NO_ACTION;
14040
14041     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14042     {
14043       if (player->push_delay_value == -1 || !player_was_pushing)
14044         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14045     }
14046     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14047     {
14048       if (player->push_delay_value == -1)
14049         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14050     }
14051     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14052     {
14053       if (!player->is_pushing)
14054         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14055     }
14056
14057     player->is_pushing = TRUE;
14058     player->is_active = TRUE;
14059
14060     if (!(IN_LEV_FIELD(nextx, nexty) &&
14061           (IS_FREE(nextx, nexty) ||
14062            (IS_SB_ELEMENT(element) &&
14063             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14064            (IS_CUSTOM_ELEMENT(element) &&
14065             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
14066       return MP_NO_ACTION;
14067
14068     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14069       return MP_NO_ACTION;
14070
14071     if (player->push_delay == -1)       // new pushing; restart delay
14072       player->push_delay = 0;
14073
14074     if (player->push_delay < player->push_delay_value &&
14075         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14076         element != EL_SPRING && element != EL_BALLOON)
14077     {
14078       // make sure that there is no move delay before next try to push
14079       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14080         player->move_delay = 0;
14081
14082       return MP_NO_ACTION;
14083     }
14084
14085     if (IS_CUSTOM_ELEMENT(element) &&
14086         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14087     {
14088       if (!DigFieldByCE(nextx, nexty, element))
14089         return MP_NO_ACTION;
14090     }
14091
14092     if (IS_SB_ELEMENT(element))
14093     {
14094       boolean sokoban_task_solved = FALSE;
14095
14096       if (element == EL_SOKOBAN_FIELD_FULL)
14097       {
14098         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14099
14100         IncrementSokobanFieldsNeeded();
14101         IncrementSokobanObjectsNeeded();
14102       }
14103
14104       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14105       {
14106         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14107
14108         DecrementSokobanFieldsNeeded();
14109         DecrementSokobanObjectsNeeded();
14110
14111         // sokoban object was pushed from empty field to sokoban field
14112         if (Back[x][y] == EL_EMPTY)
14113           sokoban_task_solved = TRUE;
14114       }
14115
14116       Feld[x][y] = EL_SOKOBAN_OBJECT;
14117
14118       if (Back[x][y] == Back[nextx][nexty])
14119         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14120       else if (Back[x][y] != 0)
14121         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14122                                     ACTION_EMPTYING);
14123       else
14124         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14125                                     ACTION_FILLING);
14126
14127       if (sokoban_task_solved &&
14128           game.sokoban_fields_still_needed == 0 &&
14129           game.sokoban_objects_still_needed == 0 &&
14130           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
14131       {
14132         game.players_still_needed = 0;
14133
14134         LevelSolved();
14135
14136         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14137       }
14138     }
14139     else
14140       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14141
14142     InitMovingField(x, y, move_direction);
14143     GfxAction[x][y] = ACTION_PUSHING;
14144
14145     if (mode == DF_SNAP)
14146       ContinueMoving(x, y);
14147     else
14148       MovPos[x][y] = (dx != 0 ? dx : dy);
14149
14150     Pushed[x][y] = TRUE;
14151     Pushed[nextx][nexty] = TRUE;
14152
14153     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14154       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14155     else
14156       player->push_delay_value = -1;    // get new value later
14157
14158     // check for element change _after_ element has been pushed
14159     if (game.use_change_when_pushing_bug)
14160     {
14161       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14162                                  player->index_bit, dig_side);
14163       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14164                                           player->index_bit, dig_side);
14165     }
14166   }
14167   else if (IS_SWITCHABLE(element))
14168   {
14169     if (PLAYER_SWITCHING(player, x, y))
14170     {
14171       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14172                                           player->index_bit, dig_side);
14173
14174       return MP_ACTION;
14175     }
14176
14177     player->is_switching = TRUE;
14178     player->switch_x = x;
14179     player->switch_y = y;
14180
14181     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14182
14183     if (element == EL_ROBOT_WHEEL)
14184     {
14185       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14186
14187       game.robot_wheel_x = x;
14188       game.robot_wheel_y = y;
14189       game.robot_wheel_active = TRUE;
14190
14191       TEST_DrawLevelField(x, y);
14192     }
14193     else if (element == EL_SP_TERMINAL)
14194     {
14195       int xx, yy;
14196
14197       SCAN_PLAYFIELD(xx, yy)
14198       {
14199         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
14200         {
14201           Bang(xx, yy);
14202         }
14203         else if (Feld[xx][yy] == EL_SP_TERMINAL)
14204         {
14205           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14206
14207           ResetGfxAnimation(xx, yy);
14208           TEST_DrawLevelField(xx, yy);
14209         }
14210       }
14211     }
14212     else if (IS_BELT_SWITCH(element))
14213     {
14214       ToggleBeltSwitch(x, y);
14215     }
14216     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14217              element == EL_SWITCHGATE_SWITCH_DOWN ||
14218              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14219              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14220     {
14221       ToggleSwitchgateSwitch(x, y);
14222     }
14223     else if (element == EL_LIGHT_SWITCH ||
14224              element == EL_LIGHT_SWITCH_ACTIVE)
14225     {
14226       ToggleLightSwitch(x, y);
14227     }
14228     else if (element == EL_TIMEGATE_SWITCH ||
14229              element == EL_DC_TIMEGATE_SWITCH)
14230     {
14231       ActivateTimegateSwitch(x, y);
14232     }
14233     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14234              element == EL_BALLOON_SWITCH_RIGHT ||
14235              element == EL_BALLOON_SWITCH_UP    ||
14236              element == EL_BALLOON_SWITCH_DOWN  ||
14237              element == EL_BALLOON_SWITCH_NONE  ||
14238              element == EL_BALLOON_SWITCH_ANY)
14239     {
14240       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14241                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14242                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14243                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14244                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14245                              move_direction);
14246     }
14247     else if (element == EL_LAMP)
14248     {
14249       Feld[x][y] = EL_LAMP_ACTIVE;
14250       game.lights_still_needed--;
14251
14252       ResetGfxAnimation(x, y);
14253       TEST_DrawLevelField(x, y);
14254     }
14255     else if (element == EL_TIME_ORB_FULL)
14256     {
14257       Feld[x][y] = EL_TIME_ORB_EMPTY;
14258
14259       if (level.time > 0 || level.use_time_orb_bug)
14260       {
14261         TimeLeft += level.time_orb_time;
14262         game.no_time_limit = FALSE;
14263
14264         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14265
14266         DisplayGameControlValues();
14267       }
14268
14269       ResetGfxAnimation(x, y);
14270       TEST_DrawLevelField(x, y);
14271     }
14272     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14273              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14274     {
14275       int xx, yy;
14276
14277       game.ball_active = !game.ball_active;
14278
14279       SCAN_PLAYFIELD(xx, yy)
14280       {
14281         int e = Feld[xx][yy];
14282
14283         if (game.ball_active)
14284         {
14285           if (e == EL_EMC_MAGIC_BALL)
14286             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14287           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14288             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14289         }
14290         else
14291         {
14292           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14293             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14294           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14295             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14296         }
14297       }
14298     }
14299
14300     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14301                                         player->index_bit, dig_side);
14302
14303     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14304                                         player->index_bit, dig_side);
14305
14306     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14307                                         player->index_bit, dig_side);
14308
14309     return MP_ACTION;
14310   }
14311   else
14312   {
14313     if (!PLAYER_SWITCHING(player, x, y))
14314     {
14315       player->is_switching = TRUE;
14316       player->switch_x = x;
14317       player->switch_y = y;
14318
14319       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14320                                  player->index_bit, dig_side);
14321       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14322                                           player->index_bit, dig_side);
14323
14324       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14325                                  player->index_bit, dig_side);
14326       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14327                                           player->index_bit, dig_side);
14328     }
14329
14330     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14331                                player->index_bit, dig_side);
14332     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14333                                         player->index_bit, dig_side);
14334
14335     return MP_NO_ACTION;
14336   }
14337
14338   player->push_delay = -1;
14339
14340   if (is_player)                // function can also be called by EL_PENGUIN
14341   {
14342     if (Feld[x][y] != element)          // really digged/collected something
14343     {
14344       player->is_collecting = !player->is_digging;
14345       player->is_active = TRUE;
14346     }
14347   }
14348
14349   return MP_MOVING;
14350 }
14351
14352 static boolean DigFieldByCE(int x, int y, int digging_element)
14353 {
14354   int element = Feld[x][y];
14355
14356   if (!IS_FREE(x, y))
14357   {
14358     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14359                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14360                   ACTION_BREAKING);
14361
14362     // no element can dig solid indestructible elements
14363     if (IS_INDESTRUCTIBLE(element) &&
14364         !IS_DIGGABLE(element) &&
14365         !IS_COLLECTIBLE(element))
14366       return FALSE;
14367
14368     if (AmoebaNr[x][y] &&
14369         (element == EL_AMOEBA_FULL ||
14370          element == EL_BD_AMOEBA ||
14371          element == EL_AMOEBA_GROWING))
14372     {
14373       AmoebaCnt[AmoebaNr[x][y]]--;
14374       AmoebaCnt2[AmoebaNr[x][y]]--;
14375     }
14376
14377     if (IS_MOVING(x, y))
14378       RemoveMovingField(x, y);
14379     else
14380     {
14381       RemoveField(x, y);
14382       TEST_DrawLevelField(x, y);
14383     }
14384
14385     // if digged element was about to explode, prevent the explosion
14386     ExplodeField[x][y] = EX_TYPE_NONE;
14387
14388     PlayLevelSoundAction(x, y, action);
14389   }
14390
14391   Store[x][y] = EL_EMPTY;
14392
14393   // this makes it possible to leave the removed element again
14394   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14395     Store[x][y] = element;
14396
14397   return TRUE;
14398 }
14399
14400 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14401 {
14402   int jx = player->jx, jy = player->jy;
14403   int x = jx + dx, y = jy + dy;
14404   int snap_direction = (dx == -1 ? MV_LEFT  :
14405                         dx == +1 ? MV_RIGHT :
14406                         dy == -1 ? MV_UP    :
14407                         dy == +1 ? MV_DOWN  : MV_NONE);
14408   boolean can_continue_snapping = (level.continuous_snapping &&
14409                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14410
14411   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14412     return FALSE;
14413
14414   if (!player->active || !IN_LEV_FIELD(x, y))
14415     return FALSE;
14416
14417   if (dx && dy)
14418     return FALSE;
14419
14420   if (!dx && !dy)
14421   {
14422     if (player->MovPos == 0)
14423       player->is_pushing = FALSE;
14424
14425     player->is_snapping = FALSE;
14426
14427     if (player->MovPos == 0)
14428     {
14429       player->is_moving = FALSE;
14430       player->is_digging = FALSE;
14431       player->is_collecting = FALSE;
14432     }
14433
14434     return FALSE;
14435   }
14436
14437   // prevent snapping with already pressed snap key when not allowed
14438   if (player->is_snapping && !can_continue_snapping)
14439     return FALSE;
14440
14441   player->MovDir = snap_direction;
14442
14443   if (player->MovPos == 0)
14444   {
14445     player->is_moving = FALSE;
14446     player->is_digging = FALSE;
14447     player->is_collecting = FALSE;
14448   }
14449
14450   player->is_dropping = FALSE;
14451   player->is_dropping_pressed = FALSE;
14452   player->drop_pressed_delay = 0;
14453
14454   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14455     return FALSE;
14456
14457   player->is_snapping = TRUE;
14458   player->is_active = TRUE;
14459
14460   if (player->MovPos == 0)
14461   {
14462     player->is_moving = FALSE;
14463     player->is_digging = FALSE;
14464     player->is_collecting = FALSE;
14465   }
14466
14467   if (player->MovPos != 0)      // prevent graphic bugs in versions < 2.2.0
14468     TEST_DrawLevelField(player->last_jx, player->last_jy);
14469
14470   TEST_DrawLevelField(x, y);
14471
14472   return TRUE;
14473 }
14474
14475 static boolean DropElement(struct PlayerInfo *player)
14476 {
14477   int old_element, new_element;
14478   int dropx = player->jx, dropy = player->jy;
14479   int drop_direction = player->MovDir;
14480   int drop_side = drop_direction;
14481   int drop_element = get_next_dropped_element(player);
14482
14483   /* do not drop an element on top of another element; when holding drop key
14484      pressed without moving, dropped element must move away before the next
14485      element can be dropped (this is especially important if the next element
14486      is dynamite, which can be placed on background for historical reasons) */
14487   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14488     return MP_ACTION;
14489
14490   if (IS_THROWABLE(drop_element))
14491   {
14492     dropx += GET_DX_FROM_DIR(drop_direction);
14493     dropy += GET_DY_FROM_DIR(drop_direction);
14494
14495     if (!IN_LEV_FIELD(dropx, dropy))
14496       return FALSE;
14497   }
14498
14499   old_element = Feld[dropx][dropy];     // old element at dropping position
14500   new_element = drop_element;           // default: no change when dropping
14501
14502   // check if player is active, not moving and ready to drop
14503   if (!player->active || player->MovPos || player->drop_delay > 0)
14504     return FALSE;
14505
14506   // check if player has anything that can be dropped
14507   if (new_element == EL_UNDEFINED)
14508     return FALSE;
14509
14510   // only set if player has anything that can be dropped
14511   player->is_dropping_pressed = TRUE;
14512
14513   // check if drop key was pressed long enough for EM style dynamite
14514   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14515     return FALSE;
14516
14517   // check if anything can be dropped at the current position
14518   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14519     return FALSE;
14520
14521   // collected custom elements can only be dropped on empty fields
14522   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14523     return FALSE;
14524
14525   if (old_element != EL_EMPTY)
14526     Back[dropx][dropy] = old_element;   // store old element on this field
14527
14528   ResetGfxAnimation(dropx, dropy);
14529   ResetRandomAnimationValue(dropx, dropy);
14530
14531   if (player->inventory_size > 0 ||
14532       player->inventory_infinite_element != EL_UNDEFINED)
14533   {
14534     if (player->inventory_size > 0)
14535     {
14536       player->inventory_size--;
14537
14538       DrawGameDoorValues();
14539
14540       if (new_element == EL_DYNAMITE)
14541         new_element = EL_DYNAMITE_ACTIVE;
14542       else if (new_element == EL_EM_DYNAMITE)
14543         new_element = EL_EM_DYNAMITE_ACTIVE;
14544       else if (new_element == EL_SP_DISK_RED)
14545         new_element = EL_SP_DISK_RED_ACTIVE;
14546     }
14547
14548     Feld[dropx][dropy] = new_element;
14549
14550     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14551       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14552                           el2img(Feld[dropx][dropy]), 0);
14553
14554     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14555
14556     // needed if previous element just changed to "empty" in the last frame
14557     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14558
14559     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14560                                player->index_bit, drop_side);
14561     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14562                                         CE_PLAYER_DROPS_X,
14563                                         player->index_bit, drop_side);
14564
14565     TestIfElementTouchesCustomElement(dropx, dropy);
14566   }
14567   else          // player is dropping a dyna bomb
14568   {
14569     player->dynabombs_left--;
14570
14571     Feld[dropx][dropy] = new_element;
14572
14573     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14574       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14575                           el2img(Feld[dropx][dropy]), 0);
14576
14577     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14578   }
14579
14580   if (Feld[dropx][dropy] == new_element) // uninitialized unless CE change
14581     InitField_WithBug1(dropx, dropy, FALSE);
14582
14583   new_element = Feld[dropx][dropy];     // element might have changed
14584
14585   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14586       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14587   {
14588     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14589       MovDir[dropx][dropy] = drop_direction;
14590
14591     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14592
14593     // do not cause impact style collision by dropping elements that can fall
14594     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14595   }
14596
14597   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14598   player->is_dropping = TRUE;
14599
14600   player->drop_pressed_delay = 0;
14601   player->is_dropping_pressed = FALSE;
14602
14603   player->drop_x = dropx;
14604   player->drop_y = dropy;
14605
14606   return TRUE;
14607 }
14608
14609 // ----------------------------------------------------------------------------
14610 // game sound playing functions
14611 // ----------------------------------------------------------------------------
14612
14613 static int *loop_sound_frame = NULL;
14614 static int *loop_sound_volume = NULL;
14615
14616 void InitPlayLevelSound(void)
14617 {
14618   int num_sounds = getSoundListSize();
14619
14620   checked_free(loop_sound_frame);
14621   checked_free(loop_sound_volume);
14622
14623   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14624   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14625 }
14626
14627 static void PlayLevelSound(int x, int y, int nr)
14628 {
14629   int sx = SCREENX(x), sy = SCREENY(y);
14630   int volume, stereo_position;
14631   int max_distance = 8;
14632   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14633
14634   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14635       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14636     return;
14637
14638   if (!IN_LEV_FIELD(x, y) ||
14639       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14640       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14641     return;
14642
14643   volume = SOUND_MAX_VOLUME;
14644
14645   if (!IN_SCR_FIELD(sx, sy))
14646   {
14647     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14648     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14649
14650     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14651   }
14652
14653   stereo_position = (SOUND_MAX_LEFT +
14654                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14655                      (SCR_FIELDX + 2 * max_distance));
14656
14657   if (IS_LOOP_SOUND(nr))
14658   {
14659     /* This assures that quieter loop sounds do not overwrite louder ones,
14660        while restarting sound volume comparison with each new game frame. */
14661
14662     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14663       return;
14664
14665     loop_sound_volume[nr] = volume;
14666     loop_sound_frame[nr] = FrameCounter;
14667   }
14668
14669   PlaySoundExt(nr, volume, stereo_position, type);
14670 }
14671
14672 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14673 {
14674   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14675                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14676                  y < LEVELY(BY1) ? LEVELY(BY1) :
14677                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14678                  sound_action);
14679 }
14680
14681 static void PlayLevelSoundAction(int x, int y, int action)
14682 {
14683   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14684 }
14685
14686 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14687 {
14688   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14689
14690   if (sound_effect != SND_UNDEFINED)
14691     PlayLevelSound(x, y, sound_effect);
14692 }
14693
14694 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14695                                               int action)
14696 {
14697   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14698
14699   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14700     PlayLevelSound(x, y, sound_effect);
14701 }
14702
14703 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14704 {
14705   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14706
14707   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14708     PlayLevelSound(x, y, sound_effect);
14709 }
14710
14711 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14712 {
14713   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14714
14715   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14716     StopSound(sound_effect);
14717 }
14718
14719 static int getLevelMusicNr(void)
14720 {
14721   if (levelset.music[level_nr] != MUS_UNDEFINED)
14722     return levelset.music[level_nr];            // from config file
14723   else
14724     return MAP_NOCONF_MUSIC(level_nr);          // from music dir
14725 }
14726
14727 static void FadeLevelSounds(void)
14728 {
14729   FadeSounds();
14730 }
14731
14732 static void FadeLevelMusic(void)
14733 {
14734   int music_nr = getLevelMusicNr();
14735   char *curr_music = getCurrentlyPlayingMusicFilename();
14736   char *next_music = getMusicInfoEntryFilename(music_nr);
14737
14738   if (!strEqual(curr_music, next_music))
14739     FadeMusic();
14740 }
14741
14742 void FadeLevelSoundsAndMusic(void)
14743 {
14744   FadeLevelSounds();
14745   FadeLevelMusic();
14746 }
14747
14748 static void PlayLevelMusic(void)
14749 {
14750   int music_nr = getLevelMusicNr();
14751   char *curr_music = getCurrentlyPlayingMusicFilename();
14752   char *next_music = getMusicInfoEntryFilename(music_nr);
14753
14754   if (!strEqual(curr_music, next_music))
14755     PlayMusicLoop(music_nr);
14756 }
14757
14758 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14759 {
14760   int element = (element_em > -1 ? map_element_EM_to_RND_game(element_em) : 0);
14761   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14762   int x = xx - 1 - offset;
14763   int y = yy - 1 - offset;
14764
14765   switch (sample)
14766   {
14767     case SOUND_blank:
14768       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14769       break;
14770
14771     case SOUND_roll:
14772       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14773       break;
14774
14775     case SOUND_stone:
14776       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14777       break;
14778
14779     case SOUND_nut:
14780       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14781       break;
14782
14783     case SOUND_crack:
14784       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14785       break;
14786
14787     case SOUND_bug:
14788       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14789       break;
14790
14791     case SOUND_tank:
14792       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14793       break;
14794
14795     case SOUND_android_clone:
14796       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14797       break;
14798
14799     case SOUND_android_move:
14800       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14801       break;
14802
14803     case SOUND_spring:
14804       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14805       break;
14806
14807     case SOUND_slurp:
14808       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14809       break;
14810
14811     case SOUND_eater:
14812       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14813       break;
14814
14815     case SOUND_eater_eat:
14816       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14817       break;
14818
14819     case SOUND_alien:
14820       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14821       break;
14822
14823     case SOUND_collect:
14824       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14825       break;
14826
14827     case SOUND_diamond:
14828       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14829       break;
14830
14831     case SOUND_squash:
14832       // !!! CHECK THIS !!!
14833 #if 1
14834       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14835 #else
14836       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14837 #endif
14838       break;
14839
14840     case SOUND_wonderfall:
14841       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14842       break;
14843
14844     case SOUND_drip:
14845       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14846       break;
14847
14848     case SOUND_push:
14849       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14850       break;
14851
14852     case SOUND_dirt:
14853       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14854       break;
14855
14856     case SOUND_acid:
14857       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14858       break;
14859
14860     case SOUND_ball:
14861       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14862       break;
14863
14864     case SOUND_slide:
14865       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14866       break;
14867
14868     case SOUND_wonder:
14869       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14870       break;
14871
14872     case SOUND_door:
14873       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14874       break;
14875
14876     case SOUND_exit_open:
14877       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14878       break;
14879
14880     case SOUND_exit_leave:
14881       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14882       break;
14883
14884     case SOUND_dynamite:
14885       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14886       break;
14887
14888     case SOUND_tick:
14889       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14890       break;
14891
14892     case SOUND_press:
14893       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14894       break;
14895
14896     case SOUND_wheel:
14897       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14898       break;
14899
14900     case SOUND_boom:
14901       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14902       break;
14903
14904     case SOUND_die:
14905       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14906       break;
14907
14908     case SOUND_time:
14909       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14910       break;
14911
14912     default:
14913       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14914       break;
14915   }
14916 }
14917
14918 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
14919 {
14920   int element = map_element_SP_to_RND(element_sp);
14921   int action = map_action_SP_to_RND(action_sp);
14922   int offset = (setup.sp_show_border_elements ? 0 : 1);
14923   int x = xx - offset;
14924   int y = yy - offset;
14925
14926   PlayLevelSoundElementAction(x, y, element, action);
14927 }
14928
14929 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
14930 {
14931   int element = map_element_MM_to_RND(element_mm);
14932   int action = map_action_MM_to_RND(action_mm);
14933   int offset = 0;
14934   int x = xx - offset;
14935   int y = yy - offset;
14936
14937   if (!IS_MM_ELEMENT(element))
14938     element = EL_MM_DEFAULT;
14939
14940   PlayLevelSoundElementAction(x, y, element, action);
14941 }
14942
14943 void PlaySound_MM(int sound_mm)
14944 {
14945   int sound = map_sound_MM_to_RND(sound_mm);
14946
14947   if (sound == SND_UNDEFINED)
14948     return;
14949
14950   PlaySound(sound);
14951 }
14952
14953 void PlaySoundLoop_MM(int sound_mm)
14954 {
14955   int sound = map_sound_MM_to_RND(sound_mm);
14956
14957   if (sound == SND_UNDEFINED)
14958     return;
14959
14960   PlaySoundLoop(sound);
14961 }
14962
14963 void StopSound_MM(int sound_mm)
14964 {
14965   int sound = map_sound_MM_to_RND(sound_mm);
14966
14967   if (sound == SND_UNDEFINED)
14968     return;
14969
14970   StopSound(sound);
14971 }
14972
14973 void RaiseScore(int value)
14974 {
14975   game.score += value;
14976
14977   game_panel_controls[GAME_PANEL_SCORE].value = game.score;
14978
14979   DisplayGameControlValues();
14980 }
14981
14982 void RaiseScoreElement(int element)
14983 {
14984   switch (element)
14985   {
14986     case EL_EMERALD:
14987     case EL_BD_DIAMOND:
14988     case EL_EMERALD_YELLOW:
14989     case EL_EMERALD_RED:
14990     case EL_EMERALD_PURPLE:
14991     case EL_SP_INFOTRON:
14992       RaiseScore(level.score[SC_EMERALD]);
14993       break;
14994     case EL_DIAMOND:
14995       RaiseScore(level.score[SC_DIAMOND]);
14996       break;
14997     case EL_CRYSTAL:
14998       RaiseScore(level.score[SC_CRYSTAL]);
14999       break;
15000     case EL_PEARL:
15001       RaiseScore(level.score[SC_PEARL]);
15002       break;
15003     case EL_BUG:
15004     case EL_BD_BUTTERFLY:
15005     case EL_SP_ELECTRON:
15006       RaiseScore(level.score[SC_BUG]);
15007       break;
15008     case EL_SPACESHIP:
15009     case EL_BD_FIREFLY:
15010     case EL_SP_SNIKSNAK:
15011       RaiseScore(level.score[SC_SPACESHIP]);
15012       break;
15013     case EL_YAMYAM:
15014     case EL_DARK_YAMYAM:
15015       RaiseScore(level.score[SC_YAMYAM]);
15016       break;
15017     case EL_ROBOT:
15018       RaiseScore(level.score[SC_ROBOT]);
15019       break;
15020     case EL_PACMAN:
15021       RaiseScore(level.score[SC_PACMAN]);
15022       break;
15023     case EL_NUT:
15024       RaiseScore(level.score[SC_NUT]);
15025       break;
15026     case EL_DYNAMITE:
15027     case EL_EM_DYNAMITE:
15028     case EL_SP_DISK_RED:
15029     case EL_DYNABOMB_INCREASE_NUMBER:
15030     case EL_DYNABOMB_INCREASE_SIZE:
15031     case EL_DYNABOMB_INCREASE_POWER:
15032       RaiseScore(level.score[SC_DYNAMITE]);
15033       break;
15034     case EL_SHIELD_NORMAL:
15035     case EL_SHIELD_DEADLY:
15036       RaiseScore(level.score[SC_SHIELD]);
15037       break;
15038     case EL_EXTRA_TIME:
15039       RaiseScore(level.extra_time_score);
15040       break;
15041     case EL_KEY_1:
15042     case EL_KEY_2:
15043     case EL_KEY_3:
15044     case EL_KEY_4:
15045     case EL_EM_KEY_1:
15046     case EL_EM_KEY_2:
15047     case EL_EM_KEY_3:
15048     case EL_EM_KEY_4:
15049     case EL_EMC_KEY_5:
15050     case EL_EMC_KEY_6:
15051     case EL_EMC_KEY_7:
15052     case EL_EMC_KEY_8:
15053     case EL_DC_KEY_WHITE:
15054       RaiseScore(level.score[SC_KEY]);
15055       break;
15056     default:
15057       RaiseScore(element_info[element].collect_score);
15058       break;
15059   }
15060 }
15061
15062 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15063 {
15064   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15065   {
15066     // closing door required in case of envelope style request dialogs
15067     if (!skip_request)
15068     {
15069       // prevent short reactivation of overlay buttons while closing door
15070       SetOverlayActive(FALSE);
15071
15072       CloseDoor(DOOR_CLOSE_1);
15073     }
15074
15075     if (network.enabled)
15076       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15077     else
15078     {
15079       if (quick_quit)
15080         FadeSkipNextFadeIn();
15081
15082       SetGameStatus(GAME_MODE_MAIN);
15083
15084       DrawMainMenu();
15085     }
15086   }
15087   else          // continue playing the game
15088   {
15089     if (tape.playing && tape.deactivate_display)
15090       TapeDeactivateDisplayOff(TRUE);
15091
15092     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15093
15094     if (tape.playing && tape.deactivate_display)
15095       TapeDeactivateDisplayOn();
15096   }
15097 }
15098
15099 void RequestQuitGame(boolean ask_if_really_quit)
15100 {
15101   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
15102   boolean skip_request = game.all_players_gone || quick_quit;
15103
15104   RequestQuitGameExt(skip_request, quick_quit,
15105                      "Do you really want to quit the game?");
15106 }
15107
15108 void RequestRestartGame(char *message)
15109 {
15110   game.restart_game_message = NULL;
15111
15112   boolean has_started_game = hasStartedNetworkGame();
15113   int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM);
15114
15115   if (Request(message, request_mode | REQ_STAY_CLOSED) && has_started_game)
15116   {
15117     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15118   }
15119   else
15120   {
15121     SetGameStatus(GAME_MODE_MAIN);
15122
15123     DrawMainMenu();
15124   }
15125 }
15126
15127 void CheckGameOver(void)
15128 {
15129   static boolean last_game_over = FALSE;
15130   static int game_over_delay = 0;
15131   int game_over_delay_value = 50;
15132   boolean game_over = checkGameFailed();
15133
15134   // do not handle game over if request dialog is already active
15135   if (game.request_active)
15136     return;
15137
15138   // do not ask to play again if game was never actually played
15139   if (!game.GamePlayed)
15140     return;
15141
15142   if (!game_over)
15143   {
15144     last_game_over = FALSE;
15145     game_over_delay = game_over_delay_value;
15146
15147     return;
15148   }
15149
15150   if (game_over_delay > 0)
15151   {
15152     game_over_delay--;
15153
15154     return;
15155   }
15156
15157   if (last_game_over != game_over)
15158     game.restart_game_message = (hasStartedNetworkGame() ?
15159                                  "Game over! Play it again?" :
15160                                  "Game over!");
15161
15162   last_game_over = game_over;
15163 }
15164
15165 boolean checkGameSolved(void)
15166 {
15167   // set for all game engines if level was solved
15168   return game.LevelSolved_GameEnd;
15169 }
15170
15171 boolean checkGameFailed(void)
15172 {
15173   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15174     return (game_em.game_over && !game_em.level_solved);
15175   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15176     return (game_sp.game_over && !game_sp.level_solved);
15177   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15178     return (game_mm.game_over && !game_mm.level_solved);
15179   else                          // GAME_ENGINE_TYPE_RND
15180     return (game.GameOver && !game.LevelSolved);
15181 }
15182
15183 boolean checkGameEnded(void)
15184 {
15185   return (checkGameSolved() || checkGameFailed());
15186 }
15187
15188
15189 // ----------------------------------------------------------------------------
15190 // random generator functions
15191 // ----------------------------------------------------------------------------
15192
15193 unsigned int InitEngineRandom_RND(int seed)
15194 {
15195   game.num_random_calls = 0;
15196
15197   return InitEngineRandom(seed);
15198 }
15199
15200 unsigned int RND(int max)
15201 {
15202   if (max > 0)
15203   {
15204     game.num_random_calls++;
15205
15206     return GetEngineRandom(max);
15207   }
15208
15209   return 0;
15210 }
15211
15212
15213 // ----------------------------------------------------------------------------
15214 // game engine snapshot handling functions
15215 // ----------------------------------------------------------------------------
15216
15217 struct EngineSnapshotInfo
15218 {
15219   // runtime values for custom element collect score
15220   int collect_score[NUM_CUSTOM_ELEMENTS];
15221
15222   // runtime values for group element choice position
15223   int choice_pos[NUM_GROUP_ELEMENTS];
15224
15225   // runtime values for belt position animations
15226   int belt_graphic[4][NUM_BELT_PARTS];
15227   int belt_anim_mode[4][NUM_BELT_PARTS];
15228 };
15229
15230 static struct EngineSnapshotInfo engine_snapshot_rnd;
15231 static char *snapshot_level_identifier = NULL;
15232 static int snapshot_level_nr = -1;
15233
15234 static void SaveEngineSnapshotValues_RND(void)
15235 {
15236   static int belt_base_active_element[4] =
15237   {
15238     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15239     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15240     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15241     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15242   };
15243   int i, j;
15244
15245   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15246   {
15247     int element = EL_CUSTOM_START + i;
15248
15249     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15250   }
15251
15252   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15253   {
15254     int element = EL_GROUP_START + i;
15255
15256     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15257   }
15258
15259   for (i = 0; i < 4; i++)
15260   {
15261     for (j = 0; j < NUM_BELT_PARTS; j++)
15262     {
15263       int element = belt_base_active_element[i] + j;
15264       int graphic = el2img(element);
15265       int anim_mode = graphic_info[graphic].anim_mode;
15266
15267       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15268       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15269     }
15270   }
15271 }
15272
15273 static void LoadEngineSnapshotValues_RND(void)
15274 {
15275   unsigned int num_random_calls = game.num_random_calls;
15276   int i, j;
15277
15278   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15279   {
15280     int element = EL_CUSTOM_START + i;
15281
15282     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15283   }
15284
15285   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15286   {
15287     int element = EL_GROUP_START + i;
15288
15289     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15290   }
15291
15292   for (i = 0; i < 4; i++)
15293   {
15294     for (j = 0; j < NUM_BELT_PARTS; j++)
15295     {
15296       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15297       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15298
15299       graphic_info[graphic].anim_mode = anim_mode;
15300     }
15301   }
15302
15303   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15304   {
15305     InitRND(tape.random_seed);
15306     for (i = 0; i < num_random_calls; i++)
15307       RND(1);
15308   }
15309
15310   if (game.num_random_calls != num_random_calls)
15311   {
15312     Error(ERR_INFO, "number of random calls out of sync");
15313     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
15314     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
15315     Error(ERR_EXIT, "this should not happen -- please debug");
15316   }
15317 }
15318
15319 void FreeEngineSnapshotSingle(void)
15320 {
15321   FreeSnapshotSingle();
15322
15323   setString(&snapshot_level_identifier, NULL);
15324   snapshot_level_nr = -1;
15325 }
15326
15327 void FreeEngineSnapshotList(void)
15328 {
15329   FreeSnapshotList();
15330 }
15331
15332 static ListNode *SaveEngineSnapshotBuffers(void)
15333 {
15334   ListNode *buffers = NULL;
15335
15336   // copy some special values to a structure better suited for the snapshot
15337
15338   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15339     SaveEngineSnapshotValues_RND();
15340   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15341     SaveEngineSnapshotValues_EM();
15342   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15343     SaveEngineSnapshotValues_SP(&buffers);
15344   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15345     SaveEngineSnapshotValues_MM(&buffers);
15346
15347   // save values stored in special snapshot structure
15348
15349   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15350     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15351   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15352     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15353   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15354     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15355   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15356     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15357
15358   // save further RND engine values
15359
15360   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15361   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15362   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15363
15364   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15365   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15366   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15367   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15368   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15369
15370   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15371   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15372   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15373
15374   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15375
15376   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15377   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15378
15379   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Feld));
15380   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15381   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15382   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15383   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15384   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15385   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15386   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15387   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15388   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15389   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15390   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15391   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15392   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15393   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15394   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15395   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15396   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15397
15398   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15399   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15400
15401   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15402   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15403   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15404
15405   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15406   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15407
15408   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15409   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15410   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15411   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15412   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15413
15414   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15415   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15416
15417 #if 0
15418   ListNode *node = engine_snapshot_list_rnd;
15419   int num_bytes = 0;
15420
15421   while (node != NULL)
15422   {
15423     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15424
15425     node = node->next;
15426   }
15427
15428   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
15429 #endif
15430
15431   return buffers;
15432 }
15433
15434 void SaveEngineSnapshotSingle(void)
15435 {
15436   ListNode *buffers = SaveEngineSnapshotBuffers();
15437
15438   // finally save all snapshot buffers to single snapshot
15439   SaveSnapshotSingle(buffers);
15440
15441   // save level identification information
15442   setString(&snapshot_level_identifier, leveldir_current->identifier);
15443   snapshot_level_nr = level_nr;
15444 }
15445
15446 boolean CheckSaveEngineSnapshotToList(void)
15447 {
15448   boolean save_snapshot =
15449     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
15450      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
15451       game.snapshot.changed_action) ||
15452      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15453       game.snapshot.collected_item));
15454
15455   game.snapshot.changed_action = FALSE;
15456   game.snapshot.collected_item = FALSE;
15457   game.snapshot.save_snapshot = save_snapshot;
15458
15459   return save_snapshot;
15460 }
15461
15462 void SaveEngineSnapshotToList(void)
15463 {
15464   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
15465       tape.quick_resume)
15466     return;
15467
15468   ListNode *buffers = SaveEngineSnapshotBuffers();
15469
15470   // finally save all snapshot buffers to snapshot list
15471   SaveSnapshotToList(buffers);
15472 }
15473
15474 void SaveEngineSnapshotToListInitial(void)
15475 {
15476   FreeEngineSnapshotList();
15477
15478   SaveEngineSnapshotToList();
15479 }
15480
15481 static void LoadEngineSnapshotValues(void)
15482 {
15483   // restore special values from snapshot structure
15484
15485   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15486     LoadEngineSnapshotValues_RND();
15487   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15488     LoadEngineSnapshotValues_EM();
15489   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15490     LoadEngineSnapshotValues_SP();
15491   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15492     LoadEngineSnapshotValues_MM();
15493 }
15494
15495 void LoadEngineSnapshotSingle(void)
15496 {
15497   LoadSnapshotSingle();
15498
15499   LoadEngineSnapshotValues();
15500 }
15501
15502 static void LoadEngineSnapshot_Undo(int steps)
15503 {
15504   LoadSnapshotFromList_Older(steps);
15505
15506   LoadEngineSnapshotValues();
15507 }
15508
15509 static void LoadEngineSnapshot_Redo(int steps)
15510 {
15511   LoadSnapshotFromList_Newer(steps);
15512
15513   LoadEngineSnapshotValues();
15514 }
15515
15516 boolean CheckEngineSnapshotSingle(void)
15517 {
15518   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15519           snapshot_level_nr == level_nr);
15520 }
15521
15522 boolean CheckEngineSnapshotList(void)
15523 {
15524   return CheckSnapshotList();
15525 }
15526
15527
15528 // ---------- new game button stuff -------------------------------------------
15529
15530 static struct
15531 {
15532   int graphic;
15533   struct XY *pos;
15534   int gadget_id;
15535   boolean *setup_value;
15536   boolean allowed_on_tape;
15537   boolean is_touch_button;
15538   char *infotext;
15539 } gamebutton_info[NUM_GAME_BUTTONS] =
15540 {
15541   {
15542     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
15543     GAME_CTRL_ID_STOP,                          NULL,
15544     TRUE, FALSE,                                "stop game"
15545   },
15546   {
15547     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
15548     GAME_CTRL_ID_PAUSE,                         NULL,
15549     TRUE, FALSE,                                "pause game"
15550   },
15551   {
15552     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
15553     GAME_CTRL_ID_PLAY,                          NULL,
15554     TRUE, FALSE,                                "play game"
15555   },
15556   {
15557     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
15558     GAME_CTRL_ID_UNDO,                          NULL,
15559     TRUE, FALSE,                                "undo step"
15560   },
15561   {
15562     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
15563     GAME_CTRL_ID_REDO,                          NULL,
15564     TRUE, FALSE,                                "redo step"
15565   },
15566   {
15567     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
15568     GAME_CTRL_ID_SAVE,                          NULL,
15569     TRUE, FALSE,                                "save game"
15570   },
15571   {
15572     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
15573     GAME_CTRL_ID_PAUSE2,                        NULL,
15574     TRUE, FALSE,                                "pause game"
15575   },
15576   {
15577     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
15578     GAME_CTRL_ID_LOAD,                          NULL,
15579     TRUE, FALSE,                                "load game"
15580   },
15581   {
15582     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
15583     GAME_CTRL_ID_PANEL_STOP,                    NULL,
15584     FALSE, FALSE,                               "stop game"
15585   },
15586   {
15587     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
15588     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
15589     FALSE, FALSE,                               "pause game"
15590   },
15591   {
15592     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
15593     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
15594     FALSE, FALSE,                               "play game"
15595   },
15596   {
15597     IMG_GFX_GAME_BUTTON_TOUCH_STOP,             &game.button.touch_stop,
15598     GAME_CTRL_ID_TOUCH_STOP,                    NULL,
15599     FALSE, TRUE,                                "stop game"
15600   },
15601   {
15602     IMG_GFX_GAME_BUTTON_TOUCH_PAUSE,            &game.button.touch_pause,
15603     GAME_CTRL_ID_TOUCH_PAUSE,                   NULL,
15604     FALSE, TRUE,                                "pause game"
15605   },
15606   {
15607     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
15608     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
15609     TRUE, FALSE,                                "background music on/off"
15610   },
15611   {
15612     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
15613     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
15614     TRUE, FALSE,                                "sound loops on/off"
15615   },
15616   {
15617     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
15618     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
15619     TRUE, FALSE,                                "normal sounds on/off"
15620   },
15621   {
15622     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
15623     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
15624     FALSE, FALSE,                               "background music on/off"
15625   },
15626   {
15627     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
15628     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
15629     FALSE, FALSE,                               "sound loops on/off"
15630   },
15631   {
15632     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
15633     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
15634     FALSE, FALSE,                               "normal sounds on/off"
15635   }
15636 };
15637
15638 void CreateGameButtons(void)
15639 {
15640   int i;
15641
15642   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15643   {
15644     int graphic = gamebutton_info[i].graphic;
15645     struct GraphicInfo *gfx = &graphic_info[graphic];
15646     struct XY *pos = gamebutton_info[i].pos;
15647     struct GadgetInfo *gi;
15648     int button_type;
15649     boolean checked;
15650     unsigned int event_mask;
15651     boolean is_touch_button = gamebutton_info[i].is_touch_button;
15652     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
15653     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
15654     int base_x = (is_touch_button ? 0 : on_tape ? VX : DX);
15655     int base_y = (is_touch_button ? 0 : on_tape ? VY : DY);
15656     int gd_x   = gfx->src_x;
15657     int gd_y   = gfx->src_y;
15658     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
15659     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
15660     int gd_xa  = gfx->src_x + gfx->active_xoffset;
15661     int gd_ya  = gfx->src_y + gfx->active_yoffset;
15662     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
15663     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
15664     int x = (is_touch_button ? pos->x : GDI_ACTIVE_POS(pos->x));
15665     int y = (is_touch_button ? pos->y : GDI_ACTIVE_POS(pos->y));
15666     int id = i;
15667
15668     if (gfx->bitmap == NULL)
15669     {
15670       game_gadget[id] = NULL;
15671
15672       continue;
15673     }
15674
15675     if (id == GAME_CTRL_ID_STOP ||
15676         id == GAME_CTRL_ID_PANEL_STOP ||
15677         id == GAME_CTRL_ID_TOUCH_STOP ||
15678         id == GAME_CTRL_ID_PLAY ||
15679         id == GAME_CTRL_ID_PANEL_PLAY ||
15680         id == GAME_CTRL_ID_SAVE ||
15681         id == GAME_CTRL_ID_LOAD)
15682     {
15683       button_type = GD_TYPE_NORMAL_BUTTON;
15684       checked = FALSE;
15685       event_mask = GD_EVENT_RELEASED;
15686     }
15687     else if (id == GAME_CTRL_ID_UNDO ||
15688              id == GAME_CTRL_ID_REDO)
15689     {
15690       button_type = GD_TYPE_NORMAL_BUTTON;
15691       checked = FALSE;
15692       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
15693     }
15694     else
15695     {
15696       button_type = GD_TYPE_CHECK_BUTTON;
15697       checked = (gamebutton_info[i].setup_value != NULL ?
15698                  *gamebutton_info[i].setup_value : FALSE);
15699       event_mask = GD_EVENT_PRESSED;
15700     }
15701
15702     gi = CreateGadget(GDI_CUSTOM_ID, id,
15703                       GDI_IMAGE_ID, graphic,
15704                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
15705                       GDI_X, base_x + x,
15706                       GDI_Y, base_y + y,
15707                       GDI_WIDTH, gfx->width,
15708                       GDI_HEIGHT, gfx->height,
15709                       GDI_TYPE, button_type,
15710                       GDI_STATE, GD_BUTTON_UNPRESSED,
15711                       GDI_CHECKED, checked,
15712                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
15713                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
15714                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
15715                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
15716                       GDI_DIRECT_DRAW, FALSE,
15717                       GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
15718                       GDI_EVENT_MASK, event_mask,
15719                       GDI_CALLBACK_ACTION, HandleGameButtons,
15720                       GDI_END);
15721
15722     if (gi == NULL)
15723       Error(ERR_EXIT, "cannot create gadget");
15724
15725     game_gadget[id] = gi;
15726   }
15727 }
15728
15729 void FreeGameButtons(void)
15730 {
15731   int i;
15732
15733   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15734     FreeGadget(game_gadget[i]);
15735 }
15736
15737 static void UnmapGameButtonsAtSamePosition(int id)
15738 {
15739   int i;
15740
15741   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15742     if (i != id &&
15743         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15744         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15745       UnmapGadget(game_gadget[i]);
15746 }
15747
15748 static void UnmapGameButtonsAtSamePosition_All(void)
15749 {
15750   if (setup.show_snapshot_buttons)
15751   {
15752     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
15753     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
15754     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
15755   }
15756   else
15757   {
15758     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
15759     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
15760     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
15761
15762     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
15763     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
15764     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
15765   }
15766 }
15767
15768 static void MapGameButtonsAtSamePosition(int id)
15769 {
15770   int i;
15771
15772   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15773     if (i != id &&
15774         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15775         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15776       MapGadget(game_gadget[i]);
15777
15778   UnmapGameButtonsAtSamePosition_All();
15779 }
15780
15781 void MapUndoRedoButtons(void)
15782 {
15783   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15784   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15785
15786   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15787   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15788 }
15789
15790 void UnmapUndoRedoButtons(void)
15791 {
15792   UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15793   UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15794
15795   MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15796   MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15797 }
15798
15799 void ModifyPauseButtons(void)
15800 {
15801   static int ids[] =
15802   {
15803     GAME_CTRL_ID_PAUSE,
15804     GAME_CTRL_ID_PAUSE2,
15805     GAME_CTRL_ID_PANEL_PAUSE,
15806     GAME_CTRL_ID_TOUCH_PAUSE,
15807     -1
15808   };
15809   int i;
15810
15811   for (i = 0; ids[i] > -1; i++)
15812     ModifyGadget(game_gadget[ids[i]], GDI_CHECKED, tape.pausing, GDI_END);
15813 }
15814
15815 static void MapGameButtonsExt(boolean on_tape)
15816 {
15817   int i;
15818
15819   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15820     if ((!on_tape || gamebutton_info[i].allowed_on_tape) &&
15821         i != GAME_CTRL_ID_UNDO &&
15822         i != GAME_CTRL_ID_REDO)
15823       MapGadget(game_gadget[i]);
15824
15825   UnmapGameButtonsAtSamePosition_All();
15826
15827   RedrawGameButtons();
15828 }
15829
15830 static void UnmapGameButtonsExt(boolean on_tape)
15831 {
15832   int i;
15833
15834   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15835     if (!on_tape || gamebutton_info[i].allowed_on_tape)
15836       UnmapGadget(game_gadget[i]);
15837 }
15838
15839 static void RedrawGameButtonsExt(boolean on_tape)
15840 {
15841   int i;
15842
15843   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15844     if (!on_tape || gamebutton_info[i].allowed_on_tape)
15845       RedrawGadget(game_gadget[i]);
15846 }
15847
15848 static void SetGadgetState(struct GadgetInfo *gi, boolean state)
15849 {
15850   if (gi == NULL)
15851     return;
15852
15853   gi->checked = state;
15854 }
15855
15856 static void RedrawSoundButtonGadget(int id)
15857 {
15858   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
15859              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
15860              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
15861              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
15862              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
15863              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
15864              id);
15865
15866   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
15867   RedrawGadget(game_gadget[id2]);
15868 }
15869
15870 void MapGameButtons(void)
15871 {
15872   MapGameButtonsExt(FALSE);
15873 }
15874
15875 void UnmapGameButtons(void)
15876 {
15877   UnmapGameButtonsExt(FALSE);
15878 }
15879
15880 void RedrawGameButtons(void)
15881 {
15882   RedrawGameButtonsExt(FALSE);
15883 }
15884
15885 void MapGameButtonsOnTape(void)
15886 {
15887   MapGameButtonsExt(TRUE);
15888 }
15889
15890 void UnmapGameButtonsOnTape(void)
15891 {
15892   UnmapGameButtonsExt(TRUE);
15893 }
15894
15895 void RedrawGameButtonsOnTape(void)
15896 {
15897   RedrawGameButtonsExt(TRUE);
15898 }
15899
15900 static void GameUndoRedoExt(void)
15901 {
15902   ClearPlayerAction();
15903
15904   tape.pausing = TRUE;
15905
15906   RedrawPlayfield();
15907   UpdateAndDisplayGameControlValues();
15908
15909   DrawCompleteVideoDisplay();
15910   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
15911   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
15912   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
15913
15914   BackToFront();
15915 }
15916
15917 static void GameUndo(int steps)
15918 {
15919   if (!CheckEngineSnapshotList())
15920     return;
15921
15922   LoadEngineSnapshot_Undo(steps);
15923
15924   GameUndoRedoExt();
15925 }
15926
15927 static void GameRedo(int steps)
15928 {
15929   if (!CheckEngineSnapshotList())
15930     return;
15931
15932   LoadEngineSnapshot_Redo(steps);
15933
15934   GameUndoRedoExt();
15935 }
15936
15937 static void HandleGameButtonsExt(int id, int button)
15938 {
15939   static boolean game_undo_executed = FALSE;
15940   int steps = BUTTON_STEPSIZE(button);
15941   boolean handle_game_buttons =
15942     (game_status == GAME_MODE_PLAYING ||
15943      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
15944
15945   if (!handle_game_buttons)
15946     return;
15947
15948   switch (id)
15949   {
15950     case GAME_CTRL_ID_STOP:
15951     case GAME_CTRL_ID_PANEL_STOP:
15952     case GAME_CTRL_ID_TOUCH_STOP:
15953       if (game_status == GAME_MODE_MAIN)
15954         break;
15955
15956       if (tape.playing)
15957         TapeStop();
15958       else
15959         RequestQuitGame(TRUE);
15960
15961       break;
15962
15963     case GAME_CTRL_ID_PAUSE:
15964     case GAME_CTRL_ID_PAUSE2:
15965     case GAME_CTRL_ID_PANEL_PAUSE:
15966     case GAME_CTRL_ID_TOUCH_PAUSE:
15967       if (network.enabled && game_status == GAME_MODE_PLAYING)
15968       {
15969         if (tape.pausing)
15970           SendToServer_ContinuePlaying();
15971         else
15972           SendToServer_PausePlaying();
15973       }
15974       else
15975         TapeTogglePause(TAPE_TOGGLE_MANUAL);
15976
15977       game_undo_executed = FALSE;
15978
15979       break;
15980
15981     case GAME_CTRL_ID_PLAY:
15982     case GAME_CTRL_ID_PANEL_PLAY:
15983       if (game_status == GAME_MODE_MAIN)
15984       {
15985         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15986       }
15987       else if (tape.pausing)
15988       {
15989         if (network.enabled)
15990           SendToServer_ContinuePlaying();
15991         else
15992           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
15993       }
15994       break;
15995
15996     case GAME_CTRL_ID_UNDO:
15997       // Important: When using "save snapshot when collecting an item" mode,
15998       // load last (current) snapshot for first "undo" after pressing "pause"
15999       // (else the last-but-one snapshot would be loaded, because the snapshot
16000       // pointer already points to the last snapshot when pressing "pause",
16001       // which is fine for "every step/move" mode, but not for "every collect")
16002       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16003           !game_undo_executed)
16004         steps--;
16005
16006       game_undo_executed = TRUE;
16007
16008       GameUndo(steps);
16009       break;
16010
16011     case GAME_CTRL_ID_REDO:
16012       GameRedo(steps);
16013       break;
16014
16015     case GAME_CTRL_ID_SAVE:
16016       TapeQuickSave();
16017       break;
16018
16019     case GAME_CTRL_ID_LOAD:
16020       TapeQuickLoad();
16021       break;
16022
16023     case SOUND_CTRL_ID_MUSIC:
16024     case SOUND_CTRL_ID_PANEL_MUSIC:
16025       if (setup.sound_music)
16026       { 
16027         setup.sound_music = FALSE;
16028
16029         FadeMusic();
16030       }
16031       else if (audio.music_available)
16032       { 
16033         setup.sound = setup.sound_music = TRUE;
16034
16035         SetAudioMode(setup.sound);
16036
16037         if (game_status == GAME_MODE_PLAYING)
16038           PlayLevelMusic();
16039       }
16040
16041       RedrawSoundButtonGadget(id);
16042
16043       break;
16044
16045     case SOUND_CTRL_ID_LOOPS:
16046     case SOUND_CTRL_ID_PANEL_LOOPS:
16047       if (setup.sound_loops)
16048         setup.sound_loops = FALSE;
16049       else if (audio.loops_available)
16050       {
16051         setup.sound = setup.sound_loops = TRUE;
16052
16053         SetAudioMode(setup.sound);
16054       }
16055
16056       RedrawSoundButtonGadget(id);
16057
16058       break;
16059
16060     case SOUND_CTRL_ID_SIMPLE:
16061     case SOUND_CTRL_ID_PANEL_SIMPLE:
16062       if (setup.sound_simple)
16063         setup.sound_simple = FALSE;
16064       else if (audio.sound_available)
16065       {
16066         setup.sound = setup.sound_simple = TRUE;
16067
16068         SetAudioMode(setup.sound);
16069       }
16070
16071       RedrawSoundButtonGadget(id);
16072
16073       break;
16074
16075     default:
16076       break;
16077   }
16078 }
16079
16080 static void HandleGameButtons(struct GadgetInfo *gi)
16081 {
16082   HandleGameButtonsExt(gi->custom_id, gi->event.button);
16083 }
16084
16085 void HandleSoundButtonKeys(Key key)
16086 {
16087   if (key == setup.shortcut.sound_simple)
16088     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
16089   else if (key == setup.shortcut.sound_loops)
16090     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
16091   else if (key == setup.shortcut.sound_music)
16092     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
16093 }