504e2d89cf341a2075fb0ed7181bc28c3a87a9e8
[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 flags for handling game actions --------------------
3284
3285   // set flags for game actions to default values
3286   game.use_key_actions = TRUE;
3287   game.use_mouse_actions = FALSE;
3288
3289   // when using Mirror Magic game engine, handle mouse events only
3290   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
3291   {
3292     game.use_key_actions = FALSE;
3293     game.use_mouse_actions = TRUE;
3294   }
3295
3296   // check for custom elements with mouse click events
3297   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3298   {
3299     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3300     {
3301       int element = EL_CUSTOM_START + i;
3302
3303       if (HAS_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
3304           HAS_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE) ||
3305           HAS_CHANGE_EVENT(element, CE_MOUSE_CLICKED_ON_X) ||
3306           HAS_CHANGE_EVENT(element, CE_MOUSE_PRESSED_ON_X))
3307         game.use_mouse_actions = TRUE;
3308     }
3309   }
3310 }
3311
3312 static int get_num_special_action(int element, int action_first,
3313                                   int action_last)
3314 {
3315   int num_special_action = 0;
3316   int i, j;
3317
3318   for (i = action_first; i <= action_last; i++)
3319   {
3320     boolean found = FALSE;
3321
3322     for (j = 0; j < NUM_DIRECTIONS; j++)
3323       if (el_act_dir2img(element, i, j) !=
3324           el_act_dir2img(element, ACTION_DEFAULT, j))
3325         found = TRUE;
3326
3327     if (found)
3328       num_special_action++;
3329     else
3330       break;
3331   }
3332
3333   return num_special_action;
3334 }
3335
3336
3337 // ============================================================================
3338 // InitGame()
3339 // ----------------------------------------------------------------------------
3340 // initialize and start new game
3341 // ============================================================================
3342
3343 #if DEBUG_INIT_PLAYER
3344 static void DebugPrintPlayerStatus(char *message)
3345 {
3346   int i;
3347
3348   if (!options.debug)
3349     return;
3350
3351   printf("%s:\n", message);
3352
3353   for (i = 0; i < MAX_PLAYERS; i++)
3354   {
3355     struct PlayerInfo *player = &stored_player[i];
3356
3357     printf("- player %d: present == %d, connected == %d [%d/%d], active == %d",
3358            i + 1,
3359            player->present,
3360            player->connected,
3361            player->connected_locally,
3362            player->connected_network,
3363            player->active);
3364
3365     if (local_player == player)
3366       printf(" (local player)");
3367
3368     printf("\n");
3369   }
3370 }
3371 #endif
3372
3373 void InitGame(void)
3374 {
3375   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3376   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3377   int fade_mask = REDRAW_FIELD;
3378
3379   boolean emulate_bd = TRUE;    // unless non-BOULDERDASH elements found
3380   boolean emulate_sb = TRUE;    // unless non-SOKOBAN     elements found
3381   boolean emulate_sp = TRUE;    // unless non-SUPAPLEX    elements found
3382   int initial_move_dir = MV_DOWN;
3383   int i, j, x, y;
3384
3385   // required here to update video display before fading (FIX THIS)
3386   DrawMaskedBorder(REDRAW_DOOR_2);
3387
3388   if (!game.restart_level)
3389     CloseDoor(DOOR_CLOSE_1);
3390
3391   SetGameStatus(GAME_MODE_PLAYING);
3392
3393   if (level_editor_test_game)
3394     FadeSkipNextFadeOut();
3395   else
3396     FadeSetEnterScreen();
3397
3398   if (CheckFadeAll())
3399     fade_mask = REDRAW_ALL;
3400
3401   FadeLevelSoundsAndMusic();
3402
3403   ExpireSoundLoops(TRUE);
3404
3405   FadeOut(fade_mask);
3406
3407   if (level_editor_test_game)
3408     FadeSkipNextFadeIn();
3409
3410   // needed if different viewport properties defined for playing
3411   ChangeViewportPropertiesIfNeeded();
3412
3413   ClearField();
3414
3415   DrawCompleteVideoDisplay();
3416
3417   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3418
3419   InitGameEngine();
3420   InitGameControlValues();
3421
3422   // initialize tape actions from game when recording tape
3423   if (tape.recording)
3424   {
3425     tape.use_key_actions   = game.use_key_actions;
3426     tape.use_mouse_actions = game.use_mouse_actions;
3427   }
3428
3429   // don't play tapes over network
3430   network_playing = (network.enabled && !tape.playing);
3431
3432   for (i = 0; i < MAX_PLAYERS; i++)
3433   {
3434     struct PlayerInfo *player = &stored_player[i];
3435
3436     player->index_nr = i;
3437     player->index_bit = (1 << i);
3438     player->element_nr = EL_PLAYER_1 + i;
3439
3440     player->present = FALSE;
3441     player->active = FALSE;
3442     player->mapped = FALSE;
3443
3444     player->killed = FALSE;
3445     player->reanimated = FALSE;
3446     player->buried = FALSE;
3447
3448     player->action = 0;
3449     player->effective_action = 0;
3450     player->programmed_action = 0;
3451     player->snap_action = 0;
3452
3453     player->mouse_action.lx = 0;
3454     player->mouse_action.ly = 0;
3455     player->mouse_action.button = 0;
3456     player->mouse_action.button_hint = 0;
3457
3458     player->effective_mouse_action.lx = 0;
3459     player->effective_mouse_action.ly = 0;
3460     player->effective_mouse_action.button = 0;
3461     player->effective_mouse_action.button_hint = 0;
3462
3463     for (j = 0; j < MAX_NUM_KEYS; j++)
3464       player->key[j] = FALSE;
3465
3466     player->num_white_keys = 0;
3467
3468     player->dynabomb_count = 0;
3469     player->dynabomb_size = 1;
3470     player->dynabombs_left = 0;
3471     player->dynabomb_xl = FALSE;
3472
3473     player->MovDir = initial_move_dir;
3474     player->MovPos = 0;
3475     player->GfxPos = 0;
3476     player->GfxDir = initial_move_dir;
3477     player->GfxAction = ACTION_DEFAULT;
3478     player->Frame = 0;
3479     player->StepFrame = 0;
3480
3481     player->initial_element = player->element_nr;
3482     player->artwork_element =
3483       (level.use_artwork_element[i] ? level.artwork_element[i] :
3484        player->element_nr);
3485     player->use_murphy = FALSE;
3486
3487     player->block_last_field = FALSE;   // initialized in InitPlayerField()
3488     player->block_delay_adjustment = 0; // initialized in InitPlayerField()
3489
3490     player->gravity = level.initial_player_gravity[i];
3491
3492     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3493
3494     player->actual_frame_counter = 0;
3495
3496     player->step_counter = 0;
3497
3498     player->last_move_dir = initial_move_dir;
3499
3500     player->is_active = FALSE;
3501
3502     player->is_waiting = FALSE;
3503     player->is_moving = FALSE;
3504     player->is_auto_moving = FALSE;
3505     player->is_digging = FALSE;
3506     player->is_snapping = FALSE;
3507     player->is_collecting = FALSE;
3508     player->is_pushing = FALSE;
3509     player->is_switching = FALSE;
3510     player->is_dropping = FALSE;
3511     player->is_dropping_pressed = FALSE;
3512
3513     player->is_bored = FALSE;
3514     player->is_sleeping = FALSE;
3515
3516     player->was_waiting = TRUE;
3517     player->was_moving = FALSE;
3518     player->was_snapping = FALSE;
3519     player->was_dropping = FALSE;
3520
3521     player->force_dropping = FALSE;
3522
3523     player->frame_counter_bored = -1;
3524     player->frame_counter_sleeping = -1;
3525
3526     player->anim_delay_counter = 0;
3527     player->post_delay_counter = 0;
3528
3529     player->dir_waiting = initial_move_dir;
3530     player->action_waiting = ACTION_DEFAULT;
3531     player->last_action_waiting = ACTION_DEFAULT;
3532     player->special_action_bored = ACTION_DEFAULT;
3533     player->special_action_sleeping = ACTION_DEFAULT;
3534
3535     player->switch_x = -1;
3536     player->switch_y = -1;
3537
3538     player->drop_x = -1;
3539     player->drop_y = -1;
3540
3541     player->show_envelope = 0;
3542
3543     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3544
3545     player->push_delay       = -1;      // initialized when pushing starts
3546     player->push_delay_value = game.initial_push_delay_value;
3547
3548     player->drop_delay = 0;
3549     player->drop_pressed_delay = 0;
3550
3551     player->last_jx = -1;
3552     player->last_jy = -1;
3553     player->jx = -1;
3554     player->jy = -1;
3555
3556     player->shield_normal_time_left = 0;
3557     player->shield_deadly_time_left = 0;
3558
3559     player->inventory_infinite_element = EL_UNDEFINED;
3560     player->inventory_size = 0;
3561
3562     if (level.use_initial_inventory[i])
3563     {
3564       for (j = 0; j < level.initial_inventory_size[i]; j++)
3565       {
3566         int element = level.initial_inventory_content[i][j];
3567         int collect_count = element_info[element].collect_count_initial;
3568         int k;
3569
3570         if (!IS_CUSTOM_ELEMENT(element))
3571           collect_count = 1;
3572
3573         if (collect_count == 0)
3574           player->inventory_infinite_element = element;
3575         else
3576           for (k = 0; k < collect_count; k++)
3577             if (player->inventory_size < MAX_INVENTORY_SIZE)
3578               player->inventory_element[player->inventory_size++] = element;
3579       }
3580     }
3581
3582     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3583     SnapField(player, 0, 0);
3584
3585     map_player_action[i] = i;
3586   }
3587
3588   network_player_action_received = FALSE;
3589
3590   // initial null action
3591   if (network_playing)
3592     SendToServer_MovePlayer(MV_NONE);
3593
3594   FrameCounter = 0;
3595   TimeFrames = 0;
3596   TimePlayed = 0;
3597   TimeLeft = level.time;
3598   TapeTime = 0;
3599
3600   ScreenMovDir = MV_NONE;
3601   ScreenMovPos = 0;
3602   ScreenGfxPos = 0;
3603
3604   ScrollStepSize = 0;   // will be correctly initialized by ScrollScreen()
3605
3606   game.robot_wheel_x = -1;
3607   game.robot_wheel_y = -1;
3608
3609   game.exit_x = -1;
3610   game.exit_y = -1;
3611
3612   game.all_players_gone = FALSE;
3613
3614   game.LevelSolved = FALSE;
3615   game.GameOver = FALSE;
3616
3617   game.GamePlayed = !tape.playing;
3618
3619   game.LevelSolved_GameWon = FALSE;
3620   game.LevelSolved_GameEnd = FALSE;
3621   game.LevelSolved_SaveTape = FALSE;
3622   game.LevelSolved_SaveScore = FALSE;
3623
3624   game.LevelSolved_CountingTime = 0;
3625   game.LevelSolved_CountingScore = 0;
3626   game.LevelSolved_CountingHealth = 0;
3627
3628   game.panel.active = TRUE;
3629
3630   game.no_time_limit = (level.time == 0);
3631
3632   game.yamyam_content_nr = 0;
3633   game.robot_wheel_active = FALSE;
3634   game.magic_wall_active = FALSE;
3635   game.magic_wall_time_left = 0;
3636   game.light_time_left = 0;
3637   game.timegate_time_left = 0;
3638   game.switchgate_pos = 0;
3639   game.wind_direction = level.wind_direction_initial;
3640
3641   game.score = 0;
3642   game.score_final = 0;
3643
3644   game.health = MAX_HEALTH;
3645   game.health_final = MAX_HEALTH;
3646
3647   game.gems_still_needed = level.gems_needed;
3648   game.sokoban_fields_still_needed = 0;
3649   game.sokoban_objects_still_needed = 0;
3650   game.lights_still_needed = 0;
3651   game.players_still_needed = 0;
3652   game.friends_still_needed = 0;
3653
3654   game.lenses_time_left = 0;
3655   game.magnify_time_left = 0;
3656
3657   game.ball_active = level.ball_active_initial;
3658   game.ball_content_nr = 0;
3659
3660   game.explosions_delayed = TRUE;
3661
3662   game.envelope_active = FALSE;
3663
3664   for (i = 0; i < NUM_BELTS; i++)
3665   {
3666     game.belt_dir[i] = MV_NONE;
3667     game.belt_dir_nr[i] = 3;            // not moving, next moving left
3668   }
3669
3670   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3671     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3672
3673 #if DEBUG_INIT_PLAYER
3674   DebugPrintPlayerStatus("Player status at level initialization");
3675 #endif
3676
3677   SCAN_PLAYFIELD(x, y)
3678   {
3679     Feld[x][y] = Last[x][y] = level.field[x][y];
3680     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3681     ChangeDelay[x][y] = 0;
3682     ChangePage[x][y] = -1;
3683     CustomValue[x][y] = 0;              // initialized in InitField()
3684     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3685     AmoebaNr[x][y] = 0;
3686     WasJustMoving[x][y] = 0;
3687     WasJustFalling[x][y] = 0;
3688     CheckCollision[x][y] = 0;
3689     CheckImpact[x][y] = 0;
3690     Stop[x][y] = FALSE;
3691     Pushed[x][y] = FALSE;
3692
3693     ChangeCount[x][y] = 0;
3694     ChangeEvent[x][y] = -1;
3695
3696     ExplodePhase[x][y] = 0;
3697     ExplodeDelay[x][y] = 0;
3698     ExplodeField[x][y] = EX_TYPE_NONE;
3699
3700     RunnerVisit[x][y] = 0;
3701     PlayerVisit[x][y] = 0;
3702
3703     GfxFrame[x][y] = 0;
3704     GfxRandom[x][y] = INIT_GFX_RANDOM();
3705     GfxElement[x][y] = EL_UNDEFINED;
3706     GfxAction[x][y] = ACTION_DEFAULT;
3707     GfxDir[x][y] = MV_NONE;
3708     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3709   }
3710
3711   SCAN_PLAYFIELD(x, y)
3712   {
3713     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3714       emulate_bd = FALSE;
3715     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3716       emulate_sb = FALSE;
3717     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3718       emulate_sp = FALSE;
3719
3720     InitField(x, y, TRUE);
3721
3722     ResetGfxAnimation(x, y);
3723   }
3724
3725   InitBeltMovement();
3726
3727   for (i = 0; i < MAX_PLAYERS; i++)
3728   {
3729     struct PlayerInfo *player = &stored_player[i];
3730
3731     // set number of special actions for bored and sleeping animation
3732     player->num_special_action_bored =
3733       get_num_special_action(player->artwork_element,
3734                              ACTION_BORING_1, ACTION_BORING_LAST);
3735     player->num_special_action_sleeping =
3736       get_num_special_action(player->artwork_element,
3737                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3738   }
3739
3740   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3741                     emulate_sb ? EMU_SOKOBAN :
3742                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3743
3744   // initialize type of slippery elements
3745   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3746   {
3747     if (!IS_CUSTOM_ELEMENT(i))
3748     {
3749       // default: elements slip down either to the left or right randomly
3750       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3751
3752       // SP style elements prefer to slip down on the left side
3753       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3754         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3755
3756       // BD style elements prefer to slip down on the left side
3757       if (game.emulation == EMU_BOULDERDASH)
3758         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3759     }
3760   }
3761
3762   // initialize explosion and ignition delay
3763   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3764   {
3765     if (!IS_CUSTOM_ELEMENT(i))
3766     {
3767       int num_phase = 8;
3768       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3769                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3770                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3771       int last_phase = (num_phase + 1) * delay;
3772       int half_phase = (num_phase / 2) * delay;
3773
3774       element_info[i].explosion_delay = last_phase - 1;
3775       element_info[i].ignition_delay = half_phase;
3776
3777       if (i == EL_BLACK_ORB)
3778         element_info[i].ignition_delay = 1;
3779     }
3780   }
3781
3782   // correct non-moving belts to start moving left
3783   for (i = 0; i < NUM_BELTS; i++)
3784     if (game.belt_dir[i] == MV_NONE)
3785       game.belt_dir_nr[i] = 3;          // not moving, next moving left
3786
3787 #if USE_NEW_PLAYER_ASSIGNMENTS
3788   // use preferred player also in local single-player mode
3789   if (!network.enabled && !game.team_mode)
3790   {
3791     int new_index_nr = setup.network_player_nr;
3792
3793     if (new_index_nr >= 0 && new_index_nr < MAX_PLAYERS)
3794     {
3795       for (i = 0; i < MAX_PLAYERS; i++)
3796         stored_player[i].connected_locally = FALSE;
3797
3798       stored_player[new_index_nr].connected_locally = TRUE;
3799     }
3800   }
3801
3802   for (i = 0; i < MAX_PLAYERS; i++)
3803   {
3804     stored_player[i].connected = FALSE;
3805
3806     // in network game mode, the local player might not be the first player
3807     if (stored_player[i].connected_locally)
3808       local_player = &stored_player[i];
3809   }
3810
3811   if (!network.enabled)
3812     local_player->connected = TRUE;
3813
3814   if (tape.playing)
3815   {
3816     for (i = 0; i < MAX_PLAYERS; i++)
3817       stored_player[i].connected = tape.player_participates[i];
3818   }
3819   else if (network.enabled)
3820   {
3821     // add team mode players connected over the network (needed for correct
3822     // assignment of player figures from level to locally playing players)
3823
3824     for (i = 0; i < MAX_PLAYERS; i++)
3825       if (stored_player[i].connected_network)
3826         stored_player[i].connected = TRUE;
3827   }
3828   else if (game.team_mode)
3829   {
3830     // try to guess locally connected team mode players (needed for correct
3831     // assignment of player figures from level to locally playing players)
3832
3833     for (i = 0; i < MAX_PLAYERS; i++)
3834       if (setup.input[i].use_joystick ||
3835           setup.input[i].key.left != KSYM_UNDEFINED)
3836         stored_player[i].connected = TRUE;
3837   }
3838
3839 #if DEBUG_INIT_PLAYER
3840   DebugPrintPlayerStatus("Player status after level initialization");
3841 #endif
3842
3843 #if DEBUG_INIT_PLAYER
3844   if (options.debug)
3845     printf("Reassigning players ...\n");
3846 #endif
3847
3848   // check if any connected player was not found in playfield
3849   for (i = 0; i < MAX_PLAYERS; i++)
3850   {
3851     struct PlayerInfo *player = &stored_player[i];
3852
3853     if (player->connected && !player->present)
3854     {
3855       struct PlayerInfo *field_player = NULL;
3856
3857 #if DEBUG_INIT_PLAYER
3858       if (options.debug)
3859         printf("- looking for field player for player %d ...\n", i + 1);
3860 #endif
3861
3862       // assign first free player found that is present in the playfield
3863
3864       // first try: look for unmapped playfield player that is not connected
3865       for (j = 0; j < MAX_PLAYERS; j++)
3866         if (field_player == NULL &&
3867             stored_player[j].present &&
3868             !stored_player[j].mapped &&
3869             !stored_player[j].connected)
3870           field_player = &stored_player[j];
3871
3872       // second try: look for *any* unmapped playfield player
3873       for (j = 0; j < MAX_PLAYERS; j++)
3874         if (field_player == NULL &&
3875             stored_player[j].present &&
3876             !stored_player[j].mapped)
3877           field_player = &stored_player[j];
3878
3879       if (field_player != NULL)
3880       {
3881         int jx = field_player->jx, jy = field_player->jy;
3882
3883 #if DEBUG_INIT_PLAYER
3884         if (options.debug)
3885           printf("- found player %d\n", field_player->index_nr + 1);
3886 #endif
3887
3888         player->present = FALSE;
3889         player->active = FALSE;
3890
3891         field_player->present = TRUE;
3892         field_player->active = TRUE;
3893
3894         /*
3895         player->initial_element = field_player->initial_element;
3896         player->artwork_element = field_player->artwork_element;
3897
3898         player->block_last_field       = field_player->block_last_field;
3899         player->block_delay_adjustment = field_player->block_delay_adjustment;
3900         */
3901
3902         StorePlayer[jx][jy] = field_player->element_nr;
3903
3904         field_player->jx = field_player->last_jx = jx;
3905         field_player->jy = field_player->last_jy = jy;
3906
3907         if (local_player == player)
3908           local_player = field_player;
3909
3910         map_player_action[field_player->index_nr] = i;
3911
3912         field_player->mapped = TRUE;
3913
3914 #if DEBUG_INIT_PLAYER
3915         if (options.debug)
3916           printf("- map_player_action[%d] == %d\n",
3917                  field_player->index_nr + 1, i + 1);
3918 #endif
3919       }
3920     }
3921
3922     if (player->connected && player->present)
3923       player->mapped = TRUE;
3924   }
3925
3926 #if DEBUG_INIT_PLAYER
3927   DebugPrintPlayerStatus("Player status after player assignment (first stage)");
3928 #endif
3929
3930 #else
3931
3932   // check if any connected player was not found in playfield
3933   for (i = 0; i < MAX_PLAYERS; i++)
3934   {
3935     struct PlayerInfo *player = &stored_player[i];
3936
3937     if (player->connected && !player->present)
3938     {
3939       for (j = 0; j < MAX_PLAYERS; j++)
3940       {
3941         struct PlayerInfo *field_player = &stored_player[j];
3942         int jx = field_player->jx, jy = field_player->jy;
3943
3944         // assign first free player found that is present in the playfield
3945         if (field_player->present && !field_player->connected)
3946         {
3947           player->present = TRUE;
3948           player->active = TRUE;
3949
3950           field_player->present = FALSE;
3951           field_player->active = FALSE;
3952
3953           player->initial_element = field_player->initial_element;
3954           player->artwork_element = field_player->artwork_element;
3955
3956           player->block_last_field       = field_player->block_last_field;
3957           player->block_delay_adjustment = field_player->block_delay_adjustment;
3958
3959           StorePlayer[jx][jy] = player->element_nr;
3960
3961           player->jx = player->last_jx = jx;
3962           player->jy = player->last_jy = jy;
3963
3964           break;
3965         }
3966       }
3967     }
3968   }
3969 #endif
3970
3971 #if 0
3972   printf("::: local_player->present == %d\n", local_player->present);
3973 #endif
3974
3975   // set focus to local player for network games, else to all players
3976   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3977   game.centered_player_nr_next = game.centered_player_nr;
3978   game.set_centered_player = FALSE;
3979   game.set_centered_player_wrap = FALSE;
3980
3981   if (network_playing && tape.recording)
3982   {
3983     // store client dependent player focus when recording network games
3984     tape.centered_player_nr_next = game.centered_player_nr_next;
3985     tape.set_centered_player = TRUE;
3986   }
3987
3988   if (tape.playing)
3989   {
3990     // when playing a tape, eliminate all players who do not participate
3991
3992 #if USE_NEW_PLAYER_ASSIGNMENTS
3993
3994     if (!game.team_mode)
3995     {
3996       for (i = 0; i < MAX_PLAYERS; i++)
3997       {
3998         if (stored_player[i].active &&
3999             !tape.player_participates[map_player_action[i]])
4000         {
4001           struct PlayerInfo *player = &stored_player[i];
4002           int jx = player->jx, jy = player->jy;
4003
4004 #if DEBUG_INIT_PLAYER
4005           if (options.debug)
4006             printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
4007 #endif
4008
4009           player->active = FALSE;
4010           StorePlayer[jx][jy] = 0;
4011           Feld[jx][jy] = EL_EMPTY;
4012         }
4013       }
4014     }
4015
4016 #else
4017
4018     for (i = 0; i < MAX_PLAYERS; i++)
4019     {
4020       if (stored_player[i].active &&
4021           !tape.player_participates[i])
4022       {
4023         struct PlayerInfo *player = &stored_player[i];
4024         int jx = player->jx, jy = player->jy;
4025
4026         player->active = FALSE;
4027         StorePlayer[jx][jy] = 0;
4028         Feld[jx][jy] = EL_EMPTY;
4029       }
4030     }
4031 #endif
4032   }
4033   else if (!network.enabled && !game.team_mode)         // && !tape.playing
4034   {
4035     // when in single player mode, eliminate all but the local player
4036
4037     for (i = 0; i < MAX_PLAYERS; i++)
4038     {
4039       struct PlayerInfo *player = &stored_player[i];
4040
4041       if (player->active && player != local_player)
4042       {
4043         int jx = player->jx, jy = player->jy;
4044
4045         player->active = FALSE;
4046         player->present = FALSE;
4047
4048         StorePlayer[jx][jy] = 0;
4049         Feld[jx][jy] = EL_EMPTY;
4050       }
4051     }
4052   }
4053
4054   for (i = 0; i < MAX_PLAYERS; i++)
4055     if (stored_player[i].active)
4056       game.players_still_needed++;
4057
4058   if (level.solved_by_one_player)
4059     game.players_still_needed = 1;
4060
4061   // when recording the game, store which players take part in the game
4062   if (tape.recording)
4063   {
4064 #if USE_NEW_PLAYER_ASSIGNMENTS
4065     for (i = 0; i < MAX_PLAYERS; i++)
4066       if (stored_player[i].connected)
4067         tape.player_participates[i] = TRUE;
4068 #else
4069     for (i = 0; i < MAX_PLAYERS; i++)
4070       if (stored_player[i].active)
4071         tape.player_participates[i] = TRUE;
4072 #endif
4073   }
4074
4075 #if DEBUG_INIT_PLAYER
4076   DebugPrintPlayerStatus("Player status after player assignment (final stage)");
4077 #endif
4078
4079   if (BorderElement == EL_EMPTY)
4080   {
4081     SBX_Left = 0;
4082     SBX_Right = lev_fieldx - SCR_FIELDX;
4083     SBY_Upper = 0;
4084     SBY_Lower = lev_fieldy - SCR_FIELDY;
4085   }
4086   else
4087   {
4088     SBX_Left = -1;
4089     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4090     SBY_Upper = -1;
4091     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4092   }
4093
4094   if (full_lev_fieldx <= SCR_FIELDX)
4095     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4096   if (full_lev_fieldy <= SCR_FIELDY)
4097     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4098
4099   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4100     SBX_Left--;
4101   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4102     SBY_Upper--;
4103
4104   // if local player not found, look for custom element that might create
4105   // the player (make some assumptions about the right custom element)
4106   if (!local_player->present)
4107   {
4108     int start_x = 0, start_y = 0;
4109     int found_rating = 0;
4110     int found_element = EL_UNDEFINED;
4111     int player_nr = local_player->index_nr;
4112
4113     SCAN_PLAYFIELD(x, y)
4114     {
4115       int element = Feld[x][y];
4116       int content;
4117       int xx, yy;
4118       boolean is_player;
4119
4120       if (level.use_start_element[player_nr] &&
4121           level.start_element[player_nr] == element &&
4122           found_rating < 4)
4123       {
4124         start_x = x;
4125         start_y = y;
4126
4127         found_rating = 4;
4128         found_element = element;
4129       }
4130
4131       if (!IS_CUSTOM_ELEMENT(element))
4132         continue;
4133
4134       if (CAN_CHANGE(element))
4135       {
4136         for (i = 0; i < element_info[element].num_change_pages; i++)
4137         {
4138           // check for player created from custom element as single target
4139           content = element_info[element].change_page[i].target_element;
4140           is_player = ELEM_IS_PLAYER(content);
4141
4142           if (is_player && (found_rating < 3 ||
4143                             (found_rating == 3 && element < found_element)))
4144           {
4145             start_x = x;
4146             start_y = y;
4147
4148             found_rating = 3;
4149             found_element = element;
4150           }
4151         }
4152       }
4153
4154       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4155       {
4156         // check for player created from custom element as explosion content
4157         content = element_info[element].content.e[xx][yy];
4158         is_player = ELEM_IS_PLAYER(content);
4159
4160         if (is_player && (found_rating < 2 ||
4161                           (found_rating == 2 && element < found_element)))
4162         {
4163           start_x = x + xx - 1;
4164           start_y = y + yy - 1;
4165
4166           found_rating = 2;
4167           found_element = element;
4168         }
4169
4170         if (!CAN_CHANGE(element))
4171           continue;
4172
4173         for (i = 0; i < element_info[element].num_change_pages; i++)
4174         {
4175           // check for player created from custom element as extended target
4176           content =
4177             element_info[element].change_page[i].target_content.e[xx][yy];
4178
4179           is_player = ELEM_IS_PLAYER(content);
4180
4181           if (is_player && (found_rating < 1 ||
4182                             (found_rating == 1 && element < found_element)))
4183           {
4184             start_x = x + xx - 1;
4185             start_y = y + yy - 1;
4186
4187             found_rating = 1;
4188             found_element = element;
4189           }
4190         }
4191       }
4192     }
4193
4194     scroll_x = SCROLL_POSITION_X(start_x);
4195     scroll_y = SCROLL_POSITION_Y(start_y);
4196   }
4197   else
4198   {
4199     scroll_x = SCROLL_POSITION_X(local_player->jx);
4200     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4201   }
4202
4203   // !!! FIX THIS (START) !!!
4204   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4205   {
4206     InitGameEngine_EM();
4207   }
4208   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4209   {
4210     InitGameEngine_SP();
4211   }
4212   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4213   {
4214     InitGameEngine_MM();
4215   }
4216   else
4217   {
4218     DrawLevel(REDRAW_FIELD);
4219     DrawAllPlayers();
4220
4221     // after drawing the level, correct some elements
4222     if (game.timegate_time_left == 0)
4223       CloseAllOpenTimegates();
4224   }
4225
4226   // blit playfield from scroll buffer to normal back buffer for fading in
4227   BlitScreenToBitmap(backbuffer);
4228   // !!! FIX THIS (END) !!!
4229
4230   DrawMaskedBorder(fade_mask);
4231
4232   FadeIn(fade_mask);
4233
4234 #if 1
4235   // full screen redraw is required at this point in the following cases:
4236   // - special editor door undrawn when game was started from level editor
4237   // - drawing area (playfield) was changed and has to be removed completely
4238   redraw_mask = REDRAW_ALL;
4239   BackToFront();
4240 #endif
4241
4242   if (!game.restart_level)
4243   {
4244     // copy default game door content to main double buffer
4245
4246     // !!! CHECK AGAIN !!!
4247     SetPanelBackground();
4248     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4249     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4250   }
4251
4252   SetPanelBackground();
4253   SetDrawBackgroundMask(REDRAW_DOOR_1);
4254
4255   UpdateAndDisplayGameControlValues();
4256
4257   if (!game.restart_level)
4258   {
4259     UnmapGameButtons();
4260     UnmapTapeButtons();
4261
4262     FreeGameButtons();
4263     CreateGameButtons();
4264
4265     MapGameButtons();
4266     MapTapeButtons();
4267
4268     // copy actual game door content to door double buffer for OpenDoor()
4269     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4270
4271     OpenDoor(DOOR_OPEN_ALL);
4272
4273     KeyboardAutoRepeatOffUnlessAutoplay();
4274
4275 #if DEBUG_INIT_PLAYER
4276     DebugPrintPlayerStatus("Player status (final)");
4277 #endif
4278   }
4279
4280   UnmapAllGadgets();
4281
4282   MapGameButtons();
4283   MapTapeButtons();
4284
4285   if (!game.restart_level && !tape.playing)
4286   {
4287     LevelStats_incPlayed(level_nr);
4288
4289     SaveLevelSetup_SeriesInfo();
4290   }
4291
4292   game.restart_level = FALSE;
4293   game.restart_game_message = NULL;
4294   game.request_active = FALSE;
4295
4296   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4297     InitGameActions_MM();
4298
4299   SaveEngineSnapshotToListInitial();
4300
4301   if (!game.restart_level)
4302   {
4303     PlaySound(SND_GAME_STARTING);
4304
4305     if (setup.sound_music)
4306       PlayLevelMusic();
4307   }
4308 }
4309
4310 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4311                         int actual_player_x, int actual_player_y)
4312 {
4313   // this is used for non-R'n'D game engines to update certain engine values
4314
4315   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4316   {
4317     actual_player_x = correctLevelPosX_EM(actual_player_x);
4318     actual_player_y = correctLevelPosY_EM(actual_player_y);
4319   }
4320
4321   // needed to determine if sounds are played within the visible screen area
4322   scroll_x = actual_scroll_x;
4323   scroll_y = actual_scroll_y;
4324
4325   // needed to get player position for "follow finger" playing input method
4326   local_player->jx = actual_player_x;
4327   local_player->jy = actual_player_y;
4328 }
4329
4330 void InitMovDir(int x, int y)
4331 {
4332   int i, element = Feld[x][y];
4333   static int xy[4][2] =
4334   {
4335     {  0, +1 },
4336     { +1,  0 },
4337     {  0, -1 },
4338     { -1,  0 }
4339   };
4340   static int direction[3][4] =
4341   {
4342     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4343     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4344     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4345   };
4346
4347   switch (element)
4348   {
4349     case EL_BUG_RIGHT:
4350     case EL_BUG_UP:
4351     case EL_BUG_LEFT:
4352     case EL_BUG_DOWN:
4353       Feld[x][y] = EL_BUG;
4354       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4355       break;
4356
4357     case EL_SPACESHIP_RIGHT:
4358     case EL_SPACESHIP_UP:
4359     case EL_SPACESHIP_LEFT:
4360     case EL_SPACESHIP_DOWN:
4361       Feld[x][y] = EL_SPACESHIP;
4362       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4363       break;
4364
4365     case EL_BD_BUTTERFLY_RIGHT:
4366     case EL_BD_BUTTERFLY_UP:
4367     case EL_BD_BUTTERFLY_LEFT:
4368     case EL_BD_BUTTERFLY_DOWN:
4369       Feld[x][y] = EL_BD_BUTTERFLY;
4370       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4371       break;
4372
4373     case EL_BD_FIREFLY_RIGHT:
4374     case EL_BD_FIREFLY_UP:
4375     case EL_BD_FIREFLY_LEFT:
4376     case EL_BD_FIREFLY_DOWN:
4377       Feld[x][y] = EL_BD_FIREFLY;
4378       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4379       break;
4380
4381     case EL_PACMAN_RIGHT:
4382     case EL_PACMAN_UP:
4383     case EL_PACMAN_LEFT:
4384     case EL_PACMAN_DOWN:
4385       Feld[x][y] = EL_PACMAN;
4386       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4387       break;
4388
4389     case EL_YAMYAM_LEFT:
4390     case EL_YAMYAM_RIGHT:
4391     case EL_YAMYAM_UP:
4392     case EL_YAMYAM_DOWN:
4393       Feld[x][y] = EL_YAMYAM;
4394       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4395       break;
4396
4397     case EL_SP_SNIKSNAK:
4398       MovDir[x][y] = MV_UP;
4399       break;
4400
4401     case EL_SP_ELECTRON:
4402       MovDir[x][y] = MV_LEFT;
4403       break;
4404
4405     case EL_MOLE_LEFT:
4406     case EL_MOLE_RIGHT:
4407     case EL_MOLE_UP:
4408     case EL_MOLE_DOWN:
4409       Feld[x][y] = EL_MOLE;
4410       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4411       break;
4412
4413     default:
4414       if (IS_CUSTOM_ELEMENT(element))
4415       {
4416         struct ElementInfo *ei = &element_info[element];
4417         int move_direction_initial = ei->move_direction_initial;
4418         int move_pattern = ei->move_pattern;
4419
4420         if (move_direction_initial == MV_START_PREVIOUS)
4421         {
4422           if (MovDir[x][y] != MV_NONE)
4423             return;
4424
4425           move_direction_initial = MV_START_AUTOMATIC;
4426         }
4427
4428         if (move_direction_initial == MV_START_RANDOM)
4429           MovDir[x][y] = 1 << RND(4);
4430         else if (move_direction_initial & MV_ANY_DIRECTION)
4431           MovDir[x][y] = move_direction_initial;
4432         else if (move_pattern == MV_ALL_DIRECTIONS ||
4433                  move_pattern == MV_TURNING_LEFT ||
4434                  move_pattern == MV_TURNING_RIGHT ||
4435                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4436                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4437                  move_pattern == MV_TURNING_RANDOM)
4438           MovDir[x][y] = 1 << RND(4);
4439         else if (move_pattern == MV_HORIZONTAL)
4440           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4441         else if (move_pattern == MV_VERTICAL)
4442           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4443         else if (move_pattern & MV_ANY_DIRECTION)
4444           MovDir[x][y] = element_info[element].move_pattern;
4445         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4446                  move_pattern == MV_ALONG_RIGHT_SIDE)
4447         {
4448           // use random direction as default start direction
4449           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4450             MovDir[x][y] = 1 << RND(4);
4451
4452           for (i = 0; i < NUM_DIRECTIONS; i++)
4453           {
4454             int x1 = x + xy[i][0];
4455             int y1 = y + xy[i][1];
4456
4457             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4458             {
4459               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4460                 MovDir[x][y] = direction[0][i];
4461               else
4462                 MovDir[x][y] = direction[1][i];
4463
4464               break;
4465             }
4466           }
4467         }                
4468       }
4469       else
4470       {
4471         MovDir[x][y] = 1 << RND(4);
4472
4473         if (element != EL_BUG &&
4474             element != EL_SPACESHIP &&
4475             element != EL_BD_BUTTERFLY &&
4476             element != EL_BD_FIREFLY)
4477           break;
4478
4479         for (i = 0; i < NUM_DIRECTIONS; i++)
4480         {
4481           int x1 = x + xy[i][0];
4482           int y1 = y + xy[i][1];
4483
4484           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4485           {
4486             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4487             {
4488               MovDir[x][y] = direction[0][i];
4489               break;
4490             }
4491             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4492                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4493             {
4494               MovDir[x][y] = direction[1][i];
4495               break;
4496             }
4497           }
4498         }
4499       }
4500       break;
4501   }
4502
4503   GfxDir[x][y] = MovDir[x][y];
4504 }
4505
4506 void InitAmoebaNr(int x, int y)
4507 {
4508   int i;
4509   int group_nr = AmoebeNachbarNr(x, y);
4510
4511   if (group_nr == 0)
4512   {
4513     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4514     {
4515       if (AmoebaCnt[i] == 0)
4516       {
4517         group_nr = i;
4518         break;
4519       }
4520     }
4521   }
4522
4523   AmoebaNr[x][y] = group_nr;
4524   AmoebaCnt[group_nr]++;
4525   AmoebaCnt2[group_nr]++;
4526 }
4527
4528 static void LevelSolved(void)
4529 {
4530   if (level.game_engine_type == GAME_ENGINE_TYPE_RND &&
4531       game.players_still_needed > 0)
4532     return;
4533
4534   game.LevelSolved = TRUE;
4535   game.GameOver = TRUE;
4536
4537   game.score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4538                       game_em.lev->score :
4539                       level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4540                       game_mm.score :
4541                       game.score);
4542   game.health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4543                        MM_HEALTH(game_mm.laser_overload_value) :
4544                        game.health);
4545
4546   game.LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed : TimeLeft);
4547   game.LevelSolved_CountingScore = game.score_final;
4548   game.LevelSolved_CountingHealth = game.health_final;
4549 }
4550
4551 void GameWon(void)
4552 {
4553   static int time_count_steps;
4554   static int time, time_final;
4555   static int score, score_final;
4556   static int health, health_final;
4557   static int game_over_delay_1 = 0;
4558   static int game_over_delay_2 = 0;
4559   static int game_over_delay_3 = 0;
4560   int game_over_delay_value_1 = 50;
4561   int game_over_delay_value_2 = 25;
4562   int game_over_delay_value_3 = 50;
4563
4564   if (!game.LevelSolved_GameWon)
4565   {
4566     int i;
4567
4568     // do not start end game actions before the player stops moving (to exit)
4569     if (local_player->active && local_player->MovPos)
4570       return;
4571
4572     game.LevelSolved_GameWon = TRUE;
4573     game.LevelSolved_SaveTape = tape.recording;
4574     game.LevelSolved_SaveScore = !tape.playing;
4575
4576     if (!tape.playing)
4577     {
4578       LevelStats_incSolved(level_nr);
4579
4580       SaveLevelSetup_SeriesInfo();
4581     }
4582
4583     if (tape.auto_play)         // tape might already be stopped here
4584       tape.auto_play_level_solved = TRUE;
4585
4586     TapeStop();
4587
4588     game_over_delay_1 = 0;
4589     game_over_delay_2 = 0;
4590     game_over_delay_3 = game_over_delay_value_3;
4591
4592     time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4593     score = score_final = game.score_final;
4594     health = health_final = game.health_final;
4595
4596     if (level.score[SC_TIME_BONUS] > 0)
4597     {
4598       if (TimeLeft > 0)
4599       {
4600         time_final = 0;
4601         score_final += TimeLeft * level.score[SC_TIME_BONUS];
4602       }
4603       else if (game.no_time_limit && TimePlayed < 999)
4604       {
4605         time_final = 999;
4606         score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4607       }
4608
4609       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4610
4611       game_over_delay_1 = game_over_delay_value_1;
4612
4613       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4614       {
4615         health_final = 0;
4616         score_final += health * level.score[SC_TIME_BONUS];
4617
4618         game_over_delay_2 = game_over_delay_value_2;
4619       }
4620
4621       game.score_final = score_final;
4622       game.health_final = health_final;
4623     }
4624
4625     if (level_editor_test_game)
4626     {
4627       time = time_final;
4628       score = score_final;
4629
4630       game.LevelSolved_CountingTime = time;
4631       game.LevelSolved_CountingScore = score;
4632
4633       game_panel_controls[GAME_PANEL_TIME].value = time;
4634       game_panel_controls[GAME_PANEL_SCORE].value = score;
4635
4636       DisplayGameControlValues();
4637     }
4638
4639     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4640     {
4641       // check if last player has left the level
4642       if (game.exit_x >= 0 &&
4643           game.exit_y >= 0)
4644       {
4645         int x = game.exit_x;
4646         int y = game.exit_y;
4647         int element = Feld[x][y];
4648
4649         // close exit door after last player
4650         if ((game.all_players_gone &&
4651              (element == EL_EXIT_OPEN ||
4652               element == EL_SP_EXIT_OPEN ||
4653               element == EL_STEEL_EXIT_OPEN)) ||
4654             element == EL_EM_EXIT_OPEN ||
4655             element == EL_EM_STEEL_EXIT_OPEN)
4656         {
4657
4658           Feld[x][y] =
4659             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4660              element == EL_EM_EXIT_OPEN         ? EL_EM_EXIT_CLOSING :
4661              element == EL_SP_EXIT_OPEN         ? EL_SP_EXIT_CLOSING:
4662              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4663              EL_EM_STEEL_EXIT_CLOSING);
4664
4665           PlayLevelSoundElementAction(x, y, element, ACTION_CLOSING);
4666         }
4667
4668         // player disappears
4669         DrawLevelField(x, y);
4670       }
4671
4672       for (i = 0; i < MAX_PLAYERS; i++)
4673       {
4674         struct PlayerInfo *player = &stored_player[i];
4675
4676         if (player->present)
4677         {
4678           RemovePlayer(player);
4679
4680           // player disappears
4681           DrawLevelField(player->jx, player->jy);
4682         }
4683       }
4684     }
4685
4686     PlaySound(SND_GAME_WINNING);
4687   }
4688
4689   if (game_over_delay_1 > 0)
4690   {
4691     game_over_delay_1--;
4692
4693     return;
4694   }
4695
4696   if (time != time_final)
4697   {
4698     int time_to_go = ABS(time_final - time);
4699     int time_count_dir = (time < time_final ? +1 : -1);
4700
4701     if (time_to_go < time_count_steps)
4702       time_count_steps = 1;
4703
4704     time  += time_count_steps * time_count_dir;
4705     score += time_count_steps * level.score[SC_TIME_BONUS];
4706
4707     game.LevelSolved_CountingTime = time;
4708     game.LevelSolved_CountingScore = score;
4709
4710     game_panel_controls[GAME_PANEL_TIME].value = time;
4711     game_panel_controls[GAME_PANEL_SCORE].value = score;
4712
4713     DisplayGameControlValues();
4714
4715     if (time == time_final)
4716       StopSound(SND_GAME_LEVELTIME_BONUS);
4717     else if (setup.sound_loops)
4718       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4719     else
4720       PlaySound(SND_GAME_LEVELTIME_BONUS);
4721
4722     return;
4723   }
4724
4725   if (game_over_delay_2 > 0)
4726   {
4727     game_over_delay_2--;
4728
4729     return;
4730   }
4731
4732   if (health != health_final)
4733   {
4734     int health_count_dir = (health < health_final ? +1 : -1);
4735
4736     health += health_count_dir;
4737     score  += level.score[SC_TIME_BONUS];
4738
4739     game.LevelSolved_CountingHealth = health;
4740     game.LevelSolved_CountingScore = score;
4741
4742     game_panel_controls[GAME_PANEL_HEALTH].value = health;
4743     game_panel_controls[GAME_PANEL_SCORE].value = score;
4744
4745     DisplayGameControlValues();
4746
4747     if (health == health_final)
4748       StopSound(SND_GAME_LEVELTIME_BONUS);
4749     else if (setup.sound_loops)
4750       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4751     else
4752       PlaySound(SND_GAME_LEVELTIME_BONUS);
4753
4754     return;
4755   }
4756
4757   game.panel.active = FALSE;
4758
4759   if (game_over_delay_3 > 0)
4760   {
4761     game_over_delay_3--;
4762
4763     return;
4764   }
4765
4766   GameEnd();
4767 }
4768
4769 void GameEnd(void)
4770 {
4771   // used instead of "level_nr" (needed for network games)
4772   int last_level_nr = levelset.level_nr;
4773   int hi_pos;
4774
4775   game.LevelSolved_GameEnd = TRUE;
4776
4777   if (game.LevelSolved_SaveTape)
4778   {
4779     // make sure that request dialog to save tape does not open door again
4780     if (!global.use_envelope_request)
4781       CloseDoor(DOOR_CLOSE_1);
4782
4783     SaveTapeChecked_LevelSolved(tape.level_nr);         // ask to save tape
4784   }
4785
4786   // if no tape is to be saved, close both doors simultaneously
4787   CloseDoor(DOOR_CLOSE_ALL);
4788
4789   if (level_editor_test_game)
4790   {
4791     SetGameStatus(GAME_MODE_MAIN);
4792
4793     DrawMainMenu();
4794
4795     return;
4796   }
4797
4798   if (!game.LevelSolved_SaveScore)
4799   {
4800     SetGameStatus(GAME_MODE_MAIN);
4801
4802     DrawMainMenu();
4803
4804     return;
4805   }
4806
4807   if (level_nr == leveldir_current->handicap_level)
4808   {
4809     leveldir_current->handicap_level++;
4810
4811     SaveLevelSetup_SeriesInfo();
4812   }
4813
4814   if (setup.increment_levels &&
4815       level_nr < leveldir_current->last_level &&
4816       !network_playing)
4817   {
4818     level_nr++;         // advance to next level
4819     TapeErase();        // start with empty tape
4820
4821     if (setup.auto_play_next_level)
4822     {
4823       LoadLevel(level_nr);
4824
4825       SaveLevelSetup_SeriesInfo();
4826     }
4827   }
4828
4829   hi_pos = NewHiScore(last_level_nr);
4830
4831   if (hi_pos >= 0 && !setup.skip_scores_after_game)
4832   {
4833     SetGameStatus(GAME_MODE_SCORES);
4834
4835     DrawHallOfFame(last_level_nr, hi_pos);
4836   }
4837   else if (setup.auto_play_next_level && setup.increment_levels &&
4838            last_level_nr < leveldir_current->last_level &&
4839            !network_playing)
4840   {
4841     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
4842   }
4843   else
4844   {
4845     SetGameStatus(GAME_MODE_MAIN);
4846
4847     DrawMainMenu();
4848   }
4849 }
4850
4851 int NewHiScore(int level_nr)
4852 {
4853   int k, l;
4854   int position = -1;
4855   boolean one_score_entry_per_name = !program.many_scores_per_name;
4856
4857   LoadScore(level_nr);
4858
4859   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4860       game.score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
4861     return -1;
4862
4863   for (k = 0; k < MAX_SCORE_ENTRIES; k++)
4864   {
4865     if (game.score_final > highscore[k].Score)
4866     {
4867       // player has made it to the hall of fame
4868
4869       if (k < MAX_SCORE_ENTRIES - 1)
4870       {
4871         int m = MAX_SCORE_ENTRIES - 1;
4872
4873         if (one_score_entry_per_name)
4874         {
4875           for (l = k; l < MAX_SCORE_ENTRIES; l++)
4876             if (strEqual(setup.player_name, highscore[l].Name))
4877               m = l;
4878
4879           if (m == k)   // player's new highscore overwrites his old one
4880             goto put_into_list;
4881         }
4882
4883         for (l = m; l > k; l--)
4884         {
4885           strcpy(highscore[l].Name, highscore[l - 1].Name);
4886           highscore[l].Score = highscore[l - 1].Score;
4887         }
4888       }
4889
4890       put_into_list:
4891
4892       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4893       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4894       highscore[k].Score = game.score_final;
4895       position = k;
4896
4897       break;
4898     }
4899     else if (one_score_entry_per_name &&
4900              !strncmp(setup.player_name, highscore[k].Name,
4901                       MAX_PLAYER_NAME_LEN))
4902       break;    // player already there with a higher score
4903   }
4904
4905   if (position >= 0) 
4906     SaveScore(level_nr);
4907
4908   return position;
4909 }
4910
4911 static int getElementMoveStepsizeExt(int x, int y, int direction)
4912 {
4913   int element = Feld[x][y];
4914   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4915   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4916   int horiz_move = (dx != 0);
4917   int sign = (horiz_move ? dx : dy);
4918   int step = sign * element_info[element].move_stepsize;
4919
4920   // special values for move stepsize for spring and things on conveyor belt
4921   if (horiz_move)
4922   {
4923     if (CAN_FALL(element) &&
4924         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4925       step = sign * MOVE_STEPSIZE_NORMAL / 2;
4926     else if (element == EL_SPRING)
4927       step = sign * MOVE_STEPSIZE_NORMAL * 2;
4928   }
4929
4930   return step;
4931 }
4932
4933 static int getElementMoveStepsize(int x, int y)
4934 {
4935   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4936 }
4937
4938 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4939 {
4940   if (player->GfxAction != action || player->GfxDir != dir)
4941   {
4942     player->GfxAction = action;
4943     player->GfxDir = dir;
4944     player->Frame = 0;
4945     player->StepFrame = 0;
4946   }
4947 }
4948
4949 static void ResetGfxFrame(int x, int y)
4950 {
4951   // profiling showed that "autotest" spends 10~20% of its time in this function
4952   if (DrawingDeactivatedField())
4953     return;
4954
4955   int element = Feld[x][y];
4956   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4957
4958   if (graphic_info[graphic].anim_global_sync)
4959     GfxFrame[x][y] = FrameCounter;
4960   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4961     GfxFrame[x][y] = CustomValue[x][y];
4962   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4963     GfxFrame[x][y] = element_info[element].collect_score;
4964   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4965     GfxFrame[x][y] = ChangeDelay[x][y];
4966 }
4967
4968 static void ResetGfxAnimation(int x, int y)
4969 {
4970   GfxAction[x][y] = ACTION_DEFAULT;
4971   GfxDir[x][y] = MovDir[x][y];
4972   GfxFrame[x][y] = 0;
4973
4974   ResetGfxFrame(x, y);
4975 }
4976
4977 static void ResetRandomAnimationValue(int x, int y)
4978 {
4979   GfxRandom[x][y] = INIT_GFX_RANDOM();
4980 }
4981
4982 static void InitMovingField(int x, int y, int direction)
4983 {
4984   int element = Feld[x][y];
4985   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4986   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4987   int newx = x + dx;
4988   int newy = y + dy;
4989   boolean is_moving_before, is_moving_after;
4990
4991   // check if element was/is moving or being moved before/after mode change
4992   is_moving_before = (WasJustMoving[x][y] != 0);
4993   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
4994
4995   // reset animation only for moving elements which change direction of moving
4996   // or which just started or stopped moving
4997   // (else CEs with property "can move" / "not moving" are reset each frame)
4998   if (is_moving_before != is_moving_after ||
4999       direction != MovDir[x][y])
5000     ResetGfxAnimation(x, y);
5001
5002   MovDir[x][y] = direction;
5003   GfxDir[x][y] = direction;
5004
5005   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5006                      direction == MV_DOWN && CAN_FALL(element) ?
5007                      ACTION_FALLING : ACTION_MOVING);
5008
5009   // this is needed for CEs with property "can move" / "not moving"
5010
5011   if (is_moving_after)
5012   {
5013     if (Feld[newx][newy] == EL_EMPTY)
5014       Feld[newx][newy] = EL_BLOCKED;
5015
5016     MovDir[newx][newy] = MovDir[x][y];
5017
5018     CustomValue[newx][newy] = CustomValue[x][y];
5019
5020     GfxFrame[newx][newy] = GfxFrame[x][y];
5021     GfxRandom[newx][newy] = GfxRandom[x][y];
5022     GfxAction[newx][newy] = GfxAction[x][y];
5023     GfxDir[newx][newy] = GfxDir[x][y];
5024   }
5025 }
5026
5027 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5028 {
5029   int direction = MovDir[x][y];
5030   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5031   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5032
5033   *goes_to_x = newx;
5034   *goes_to_y = newy;
5035 }
5036
5037 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5038 {
5039   int oldx = x, oldy = y;
5040   int direction = MovDir[x][y];
5041
5042   if (direction == MV_LEFT)
5043     oldx++;
5044   else if (direction == MV_RIGHT)
5045     oldx--;
5046   else if (direction == MV_UP)
5047     oldy++;
5048   else if (direction == MV_DOWN)
5049     oldy--;
5050
5051   *comes_from_x = oldx;
5052   *comes_from_y = oldy;
5053 }
5054
5055 static int MovingOrBlocked2Element(int x, int y)
5056 {
5057   int element = Feld[x][y];
5058
5059   if (element == EL_BLOCKED)
5060   {
5061     int oldx, oldy;
5062
5063     Blocked2Moving(x, y, &oldx, &oldy);
5064     return Feld[oldx][oldy];
5065   }
5066   else
5067     return element;
5068 }
5069
5070 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5071 {
5072   // like MovingOrBlocked2Element(), but if element is moving
5073   // and (x,y) is the field the moving element is just leaving,
5074   // return EL_BLOCKED instead of the element value
5075   int element = Feld[x][y];
5076
5077   if (IS_MOVING(x, y))
5078   {
5079     if (element == EL_BLOCKED)
5080     {
5081       int oldx, oldy;
5082
5083       Blocked2Moving(x, y, &oldx, &oldy);
5084       return Feld[oldx][oldy];
5085     }
5086     else
5087       return EL_BLOCKED;
5088   }
5089   else
5090     return element;
5091 }
5092
5093 static void RemoveField(int x, int y)
5094 {
5095   Feld[x][y] = EL_EMPTY;
5096
5097   MovPos[x][y] = 0;
5098   MovDir[x][y] = 0;
5099   MovDelay[x][y] = 0;
5100
5101   CustomValue[x][y] = 0;
5102
5103   AmoebaNr[x][y] = 0;
5104   ChangeDelay[x][y] = 0;
5105   ChangePage[x][y] = -1;
5106   Pushed[x][y] = FALSE;
5107
5108   GfxElement[x][y] = EL_UNDEFINED;
5109   GfxAction[x][y] = ACTION_DEFAULT;
5110   GfxDir[x][y] = MV_NONE;
5111 }
5112
5113 static void RemoveMovingField(int x, int y)
5114 {
5115   int oldx = x, oldy = y, newx = x, newy = y;
5116   int element = Feld[x][y];
5117   int next_element = EL_UNDEFINED;
5118
5119   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5120     return;
5121
5122   if (IS_MOVING(x, y))
5123   {
5124     Moving2Blocked(x, y, &newx, &newy);
5125
5126     if (Feld[newx][newy] != EL_BLOCKED)
5127     {
5128       // element is moving, but target field is not free (blocked), but
5129       // already occupied by something different (example: acid pool);
5130       // in this case, only remove the moving field, but not the target
5131
5132       RemoveField(oldx, oldy);
5133
5134       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5135
5136       TEST_DrawLevelField(oldx, oldy);
5137
5138       return;
5139     }
5140   }
5141   else if (element == EL_BLOCKED)
5142   {
5143     Blocked2Moving(x, y, &oldx, &oldy);
5144     if (!IS_MOVING(oldx, oldy))
5145       return;
5146   }
5147
5148   if (element == EL_BLOCKED &&
5149       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5150        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5151        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5152        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5153        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5154        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5155     next_element = get_next_element(Feld[oldx][oldy]);
5156
5157   RemoveField(oldx, oldy);
5158   RemoveField(newx, newy);
5159
5160   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5161
5162   if (next_element != EL_UNDEFINED)
5163     Feld[oldx][oldy] = next_element;
5164
5165   TEST_DrawLevelField(oldx, oldy);
5166   TEST_DrawLevelField(newx, newy);
5167 }
5168
5169 void DrawDynamite(int x, int y)
5170 {
5171   int sx = SCREENX(x), sy = SCREENY(y);
5172   int graphic = el2img(Feld[x][y]);
5173   int frame;
5174
5175   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5176     return;
5177
5178   if (IS_WALKABLE_INSIDE(Back[x][y]))
5179     return;
5180
5181   if (Back[x][y])
5182     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5183   else if (Store[x][y])
5184     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5185
5186   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5187
5188   if (Back[x][y] || Store[x][y])
5189     DrawGraphicThruMask(sx, sy, graphic, frame);
5190   else
5191     DrawGraphic(sx, sy, graphic, frame);
5192 }
5193
5194 static void CheckDynamite(int x, int y)
5195 {
5196   if (MovDelay[x][y] != 0)      // dynamite is still waiting to explode
5197   {
5198     MovDelay[x][y]--;
5199
5200     if (MovDelay[x][y] != 0)
5201     {
5202       DrawDynamite(x, y);
5203       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5204
5205       return;
5206     }
5207   }
5208
5209   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5210
5211   Bang(x, y);
5212 }
5213
5214 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5215 {
5216   boolean num_checked_players = 0;
5217   int i;
5218
5219   for (i = 0; i < MAX_PLAYERS; i++)
5220   {
5221     if (stored_player[i].active)
5222     {
5223       int sx = stored_player[i].jx;
5224       int sy = stored_player[i].jy;
5225
5226       if (num_checked_players == 0)
5227       {
5228         *sx1 = *sx2 = sx;
5229         *sy1 = *sy2 = sy;
5230       }
5231       else
5232       {
5233         *sx1 = MIN(*sx1, sx);
5234         *sy1 = MIN(*sy1, sy);
5235         *sx2 = MAX(*sx2, sx);
5236         *sy2 = MAX(*sy2, sy);
5237       }
5238
5239       num_checked_players++;
5240     }
5241   }
5242 }
5243
5244 static boolean checkIfAllPlayersFitToScreen_RND(void)
5245 {
5246   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5247
5248   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5249
5250   return (sx2 - sx1 < SCR_FIELDX &&
5251           sy2 - sy1 < SCR_FIELDY);
5252 }
5253
5254 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5255 {
5256   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5257
5258   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5259
5260   *sx = (sx1 + sx2) / 2;
5261   *sy = (sy1 + sy2) / 2;
5262 }
5263
5264 static void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5265                                boolean center_screen, boolean quick_relocation)
5266 {
5267   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5268   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5269   boolean no_delay = (tape.warp_forward);
5270   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5271   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5272   int new_scroll_x, new_scroll_y;
5273
5274   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5275   {
5276     // case 1: quick relocation inside visible screen (without scrolling)
5277
5278     RedrawPlayfield();
5279
5280     return;
5281   }
5282
5283   if (!level.shifted_relocation || center_screen)
5284   {
5285     // relocation _with_ centering of screen
5286
5287     new_scroll_x = SCROLL_POSITION_X(x);
5288     new_scroll_y = SCROLL_POSITION_Y(y);
5289   }
5290   else
5291   {
5292     // relocation _without_ centering of screen
5293
5294     int center_scroll_x = SCROLL_POSITION_X(old_x);
5295     int center_scroll_y = SCROLL_POSITION_Y(old_y);
5296     int offset_x = x + (scroll_x - center_scroll_x);
5297     int offset_y = y + (scroll_y - center_scroll_y);
5298
5299     // for new screen position, apply previous offset to center position
5300     new_scroll_x = SCROLL_POSITION_X(offset_x);
5301     new_scroll_y = SCROLL_POSITION_Y(offset_y);
5302   }
5303
5304   if (quick_relocation)
5305   {
5306     // case 2: quick relocation (redraw without visible scrolling)
5307
5308     scroll_x = new_scroll_x;
5309     scroll_y = new_scroll_y;
5310
5311     RedrawPlayfield();
5312
5313     return;
5314   }
5315
5316   // case 3: visible relocation (with scrolling to new position)
5317
5318   ScrollScreen(NULL, SCROLL_GO_ON);     // scroll last frame to full tile
5319
5320   SetVideoFrameDelay(wait_delay_value);
5321
5322   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5323   {
5324     int dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5325     int dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5326
5327     if (dx == 0 && dy == 0)             // no scrolling needed at all
5328       break;
5329
5330     scroll_x -= dx;
5331     scroll_y -= dy;
5332
5333     // set values for horizontal/vertical screen scrolling (half tile size)
5334     int dir_x = (dx != 0 ? MV_HORIZONTAL : 0);
5335     int dir_y = (dy != 0 ? MV_VERTICAL   : 0);
5336     int pos_x = dx * TILEX / 2;
5337     int pos_y = dy * TILEY / 2;
5338     int fx = getFieldbufferOffsetX_RND(dir_x, pos_x);
5339     int fy = getFieldbufferOffsetY_RND(dir_y, pos_y);
5340
5341     ScrollLevel(dx, dy);
5342     DrawAllPlayers();
5343
5344     // scroll in two steps of half tile size to make things smoother
5345     BlitScreenToBitmapExt_RND(window, fx, fy);
5346
5347     // scroll second step to align at full tile size
5348     BlitScreenToBitmap(window);
5349   }
5350
5351   DrawAllPlayers();
5352   BackToFront();
5353
5354   SetVideoFrameDelay(frame_delay_value_old);
5355 }
5356
5357 static void RelocatePlayer(int jx, int jy, int el_player_raw)
5358 {
5359   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5360   int player_nr = GET_PLAYER_NR(el_player);
5361   struct PlayerInfo *player = &stored_player[player_nr];
5362   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5363   boolean no_delay = (tape.warp_forward);
5364   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5365   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5366   int old_jx = player->jx;
5367   int old_jy = player->jy;
5368   int old_element = Feld[old_jx][old_jy];
5369   int element = Feld[jx][jy];
5370   boolean player_relocated = (old_jx != jx || old_jy != jy);
5371
5372   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5373   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5374   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5375   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5376   int leave_side_horiz = move_dir_horiz;
5377   int leave_side_vert  = move_dir_vert;
5378   int enter_side = enter_side_horiz | enter_side_vert;
5379   int leave_side = leave_side_horiz | leave_side_vert;
5380
5381   if (player->buried)           // do not reanimate dead player
5382     return;
5383
5384   if (!player_relocated)        // no need to relocate the player
5385     return;
5386
5387   if (IS_PLAYER(jx, jy))        // player already placed at new position
5388   {
5389     RemoveField(jx, jy);        // temporarily remove newly placed player
5390     DrawLevelField(jx, jy);
5391   }
5392
5393   if (player->present)
5394   {
5395     while (player->MovPos)
5396     {
5397       ScrollPlayer(player, SCROLL_GO_ON);
5398       ScrollScreen(NULL, SCROLL_GO_ON);
5399
5400       AdvanceFrameAndPlayerCounters(player->index_nr);
5401
5402       DrawPlayer(player);
5403
5404       BackToFront_WithFrameDelay(wait_delay_value);
5405     }
5406
5407     DrawPlayer(player);         // needed here only to cleanup last field
5408     DrawLevelField(player->jx, player->jy);     // remove player graphic
5409
5410     player->is_moving = FALSE;
5411   }
5412
5413   if (IS_CUSTOM_ELEMENT(old_element))
5414     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5415                                CE_LEFT_BY_PLAYER,
5416                                player->index_bit, leave_side);
5417
5418   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5419                                       CE_PLAYER_LEAVES_X,
5420                                       player->index_bit, leave_side);
5421
5422   Feld[jx][jy] = el_player;
5423   InitPlayerField(jx, jy, el_player, TRUE);
5424
5425   /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5426      possible that the relocation target field did not contain a player element,
5427      but a walkable element, to which the new player was relocated -- in this
5428      case, restore that (already initialized!) element on the player field */
5429   if (!ELEM_IS_PLAYER(element)) // player may be set on walkable element
5430   {
5431     Feld[jx][jy] = element;     // restore previously existing element
5432   }
5433
5434   // only visually relocate centered player
5435   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5436                      FALSE, level.instant_relocation);
5437
5438   TestIfPlayerTouchesBadThing(jx, jy);
5439   TestIfPlayerTouchesCustomElement(jx, jy);
5440
5441   if (IS_CUSTOM_ELEMENT(element))
5442     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5443                                player->index_bit, enter_side);
5444
5445   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5446                                       player->index_bit, enter_side);
5447
5448   if (player->is_switching)
5449   {
5450     /* ensure that relocation while still switching an element does not cause
5451        a new element to be treated as also switched directly after relocation
5452        (this is important for teleporter switches that teleport the player to
5453        a place where another teleporter switch is in the same direction, which
5454        would then incorrectly be treated as immediately switched before the
5455        direction key that caused the switch was released) */
5456
5457     player->switch_x += jx - old_jx;
5458     player->switch_y += jy - old_jy;
5459   }
5460 }
5461
5462 static void Explode(int ex, int ey, int phase, int mode)
5463 {
5464   int x, y;
5465   int last_phase;
5466   int border_element;
5467
5468   // !!! eliminate this variable !!!
5469   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5470
5471   if (game.explosions_delayed)
5472   {
5473     ExplodeField[ex][ey] = mode;
5474     return;
5475   }
5476
5477   if (phase == EX_PHASE_START)          // initialize 'Store[][]' field
5478   {
5479     int center_element = Feld[ex][ey];
5480     int artwork_element, explosion_element;     // set these values later
5481
5482     // remove things displayed in background while burning dynamite
5483     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5484       Back[ex][ey] = 0;
5485
5486     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5487     {
5488       // put moving element to center field (and let it explode there)
5489       center_element = MovingOrBlocked2Element(ex, ey);
5490       RemoveMovingField(ex, ey);
5491       Feld[ex][ey] = center_element;
5492     }
5493
5494     // now "center_element" is finally determined -- set related values now
5495     artwork_element = center_element;           // for custom player artwork
5496     explosion_element = center_element;         // for custom player artwork
5497
5498     if (IS_PLAYER(ex, ey))
5499     {
5500       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5501
5502       artwork_element = stored_player[player_nr].artwork_element;
5503
5504       if (level.use_explosion_element[player_nr])
5505       {
5506         explosion_element = level.explosion_element[player_nr];
5507         artwork_element = explosion_element;
5508       }
5509     }
5510
5511     if (mode == EX_TYPE_NORMAL ||
5512         mode == EX_TYPE_CENTER ||
5513         mode == EX_TYPE_CROSS)
5514       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5515
5516     last_phase = element_info[explosion_element].explosion_delay + 1;
5517
5518     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5519     {
5520       int xx = x - ex + 1;
5521       int yy = y - ey + 1;
5522       int element;
5523
5524       if (!IN_LEV_FIELD(x, y) ||
5525           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5526           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5527         continue;
5528
5529       element = Feld[x][y];
5530
5531       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5532       {
5533         element = MovingOrBlocked2Element(x, y);
5534
5535         if (!IS_EXPLOSION_PROOF(element))
5536           RemoveMovingField(x, y);
5537       }
5538
5539       // indestructible elements can only explode in center (but not flames)
5540       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5541                                            mode == EX_TYPE_BORDER)) ||
5542           element == EL_FLAMES)
5543         continue;
5544
5545       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5546          behaviour, for example when touching a yamyam that explodes to rocks
5547          with active deadly shield, a rock is created under the player !!! */
5548       // (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8)
5549 #if 0
5550       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5551           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5552            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5553 #else
5554       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5555 #endif
5556       {
5557         if (IS_ACTIVE_BOMB(element))
5558         {
5559           // re-activate things under the bomb like gate or penguin
5560           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5561           Back[x][y] = 0;
5562         }
5563
5564         continue;
5565       }
5566
5567       // save walkable background elements while explosion on same tile
5568       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5569           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5570         Back[x][y] = element;
5571
5572       // ignite explodable elements reached by other explosion
5573       if (element == EL_EXPLOSION)
5574         element = Store2[x][y];
5575
5576       if (AmoebaNr[x][y] &&
5577           (element == EL_AMOEBA_FULL ||
5578            element == EL_BD_AMOEBA ||
5579            element == EL_AMOEBA_GROWING))
5580       {
5581         AmoebaCnt[AmoebaNr[x][y]]--;
5582         AmoebaCnt2[AmoebaNr[x][y]]--;
5583       }
5584
5585       RemoveField(x, y);
5586
5587       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5588       {
5589         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5590
5591         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5592
5593         if (PLAYERINFO(ex, ey)->use_murphy)
5594           Store[x][y] = EL_EMPTY;
5595       }
5596
5597       // !!! check this case -- currently needed for rnd_rado_negundo_v,
5598       // !!! levels 015 018 019 020 021 022 023 026 027 028 !!!
5599       else if (ELEM_IS_PLAYER(center_element))
5600         Store[x][y] = EL_EMPTY;
5601       else if (center_element == EL_YAMYAM)
5602         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5603       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5604         Store[x][y] = element_info[center_element].content.e[xx][yy];
5605 #if 1
5606       // needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5607       // (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5608       // otherwise) -- FIX THIS !!!
5609       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5610         Store[x][y] = element_info[element].content.e[1][1];
5611 #else
5612       else if (!CAN_EXPLODE(element))
5613         Store[x][y] = element_info[element].content.e[1][1];
5614 #endif
5615       else
5616         Store[x][y] = EL_EMPTY;
5617
5618       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5619           center_element == EL_AMOEBA_TO_DIAMOND)
5620         Store2[x][y] = element;
5621
5622       Feld[x][y] = EL_EXPLOSION;
5623       GfxElement[x][y] = artwork_element;
5624
5625       ExplodePhase[x][y] = 1;
5626       ExplodeDelay[x][y] = last_phase;
5627
5628       Stop[x][y] = TRUE;
5629     }
5630
5631     if (center_element == EL_YAMYAM)
5632       game.yamyam_content_nr =
5633         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5634
5635     return;
5636   }
5637
5638   if (Stop[ex][ey])
5639     return;
5640
5641   x = ex;
5642   y = ey;
5643
5644   if (phase == 1)
5645     GfxFrame[x][y] = 0;         // restart explosion animation
5646
5647   last_phase = ExplodeDelay[x][y];
5648
5649   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5650
5651   // this can happen if the player leaves an explosion just in time
5652   if (GfxElement[x][y] == EL_UNDEFINED)
5653     GfxElement[x][y] = EL_EMPTY;
5654
5655   border_element = Store2[x][y];
5656   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5657     border_element = StorePlayer[x][y];
5658
5659   if (phase == element_info[border_element].ignition_delay ||
5660       phase == last_phase)
5661   {
5662     boolean border_explosion = FALSE;
5663
5664     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5665         !PLAYER_EXPLOSION_PROTECTED(x, y))
5666     {
5667       KillPlayerUnlessExplosionProtected(x, y);
5668       border_explosion = TRUE;
5669     }
5670     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5671     {
5672       Feld[x][y] = Store2[x][y];
5673       Store2[x][y] = 0;
5674       Bang(x, y);
5675       border_explosion = TRUE;
5676     }
5677     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5678     {
5679       AmoebeUmwandeln(x, y);
5680       Store2[x][y] = 0;
5681       border_explosion = TRUE;
5682     }
5683
5684     // if an element just explodes due to another explosion (chain-reaction),
5685     // do not immediately end the new explosion when it was the last frame of
5686     // the explosion (as it would be done in the following "if"-statement!)
5687     if (border_explosion && phase == last_phase)
5688       return;
5689   }
5690
5691   if (phase == last_phase)
5692   {
5693     int element;
5694
5695     element = Feld[x][y] = Store[x][y];
5696     Store[x][y] = Store2[x][y] = 0;
5697     GfxElement[x][y] = EL_UNDEFINED;
5698
5699     // player can escape from explosions and might therefore be still alive
5700     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5701         element <= EL_PLAYER_IS_EXPLODING_4)
5702     {
5703       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5704       int explosion_element = EL_PLAYER_1 + player_nr;
5705       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5706       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5707
5708       if (level.use_explosion_element[player_nr])
5709         explosion_element = level.explosion_element[player_nr];
5710
5711       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5712                     element_info[explosion_element].content.e[xx][yy]);
5713     }
5714
5715     // restore probably existing indestructible background element
5716     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5717       element = Feld[x][y] = Back[x][y];
5718     Back[x][y] = 0;
5719
5720     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5721     GfxDir[x][y] = MV_NONE;
5722     ChangeDelay[x][y] = 0;
5723     ChangePage[x][y] = -1;
5724
5725     CustomValue[x][y] = 0;
5726
5727     InitField_WithBug2(x, y, FALSE);
5728
5729     TEST_DrawLevelField(x, y);
5730
5731     TestIfElementTouchesCustomElement(x, y);
5732
5733     if (GFX_CRUMBLED(element))
5734       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5735
5736     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5737       StorePlayer[x][y] = 0;
5738
5739     if (ELEM_IS_PLAYER(element))
5740       RelocatePlayer(x, y, element);
5741   }
5742   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5743   {
5744     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5745     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5746
5747     if (phase == delay)
5748       TEST_DrawLevelFieldCrumbled(x, y);
5749
5750     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5751     {
5752       DrawLevelElement(x, y, Back[x][y]);
5753       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5754     }
5755     else if (IS_WALKABLE_UNDER(Back[x][y]))
5756     {
5757       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5758       DrawLevelElementThruMask(x, y, Back[x][y]);
5759     }
5760     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5761       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5762   }
5763 }
5764
5765 static void DynaExplode(int ex, int ey)
5766 {
5767   int i, j;
5768   int dynabomb_element = Feld[ex][ey];
5769   int dynabomb_size = 1;
5770   boolean dynabomb_xl = FALSE;
5771   struct PlayerInfo *player;
5772   static int xy[4][2] =
5773   {
5774     { 0, -1 },
5775     { -1, 0 },
5776     { +1, 0 },
5777     { 0, +1 }
5778   };
5779
5780   if (IS_ACTIVE_BOMB(dynabomb_element))
5781   {
5782     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5783     dynabomb_size = player->dynabomb_size;
5784     dynabomb_xl = player->dynabomb_xl;
5785     player->dynabombs_left++;
5786   }
5787
5788   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5789
5790   for (i = 0; i < NUM_DIRECTIONS; i++)
5791   {
5792     for (j = 1; j <= dynabomb_size; j++)
5793     {
5794       int x = ex + j * xy[i][0];
5795       int y = ey + j * xy[i][1];
5796       int element;
5797
5798       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5799         break;
5800
5801       element = Feld[x][y];
5802
5803       // do not restart explosions of fields with active bombs
5804       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5805         continue;
5806
5807       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5808
5809       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5810           !IS_DIGGABLE(element) && !dynabomb_xl)
5811         break;
5812     }
5813   }
5814 }
5815
5816 void Bang(int x, int y)
5817 {
5818   int element = MovingOrBlocked2Element(x, y);
5819   int explosion_type = EX_TYPE_NORMAL;
5820
5821   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5822   {
5823     struct PlayerInfo *player = PLAYERINFO(x, y);
5824
5825     element = Feld[x][y] = player->initial_element;
5826
5827     if (level.use_explosion_element[player->index_nr])
5828     {
5829       int explosion_element = level.explosion_element[player->index_nr];
5830
5831       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5832         explosion_type = EX_TYPE_CROSS;
5833       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5834         explosion_type = EX_TYPE_CENTER;
5835     }
5836   }
5837
5838   switch (element)
5839   {
5840     case EL_BUG:
5841     case EL_SPACESHIP:
5842     case EL_BD_BUTTERFLY:
5843     case EL_BD_FIREFLY:
5844     case EL_YAMYAM:
5845     case EL_DARK_YAMYAM:
5846     case EL_ROBOT:
5847     case EL_PACMAN:
5848     case EL_MOLE:
5849       RaiseScoreElement(element);
5850       break;
5851
5852     case EL_DYNABOMB_PLAYER_1_ACTIVE:
5853     case EL_DYNABOMB_PLAYER_2_ACTIVE:
5854     case EL_DYNABOMB_PLAYER_3_ACTIVE:
5855     case EL_DYNABOMB_PLAYER_4_ACTIVE:
5856     case EL_DYNABOMB_INCREASE_NUMBER:
5857     case EL_DYNABOMB_INCREASE_SIZE:
5858     case EL_DYNABOMB_INCREASE_POWER:
5859       explosion_type = EX_TYPE_DYNA;
5860       break;
5861
5862     case EL_DC_LANDMINE:
5863       explosion_type = EX_TYPE_CENTER;
5864       break;
5865
5866     case EL_PENGUIN:
5867     case EL_LAMP:
5868     case EL_LAMP_ACTIVE:
5869     case EL_AMOEBA_TO_DIAMOND:
5870       if (!IS_PLAYER(x, y))     // penguin and player may be at same field
5871         explosion_type = EX_TYPE_CENTER;
5872       break;
5873
5874     default:
5875       if (element_info[element].explosion_type == EXPLODES_CROSS)
5876         explosion_type = EX_TYPE_CROSS;
5877       else if (element_info[element].explosion_type == EXPLODES_1X1)
5878         explosion_type = EX_TYPE_CENTER;
5879       break;
5880   }
5881
5882   if (explosion_type == EX_TYPE_DYNA)
5883     DynaExplode(x, y);
5884   else
5885     Explode(x, y, EX_PHASE_START, explosion_type);
5886
5887   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5888 }
5889
5890 static void SplashAcid(int x, int y)
5891 {
5892   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5893       (!IN_LEV_FIELD(x - 1, y - 2) ||
5894        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5895     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5896
5897   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5898       (!IN_LEV_FIELD(x + 1, y - 2) ||
5899        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5900     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5901
5902   PlayLevelSound(x, y, SND_ACID_SPLASHING);
5903 }
5904
5905 static void InitBeltMovement(void)
5906 {
5907   static int belt_base_element[4] =
5908   {
5909     EL_CONVEYOR_BELT_1_LEFT,
5910     EL_CONVEYOR_BELT_2_LEFT,
5911     EL_CONVEYOR_BELT_3_LEFT,
5912     EL_CONVEYOR_BELT_4_LEFT
5913   };
5914   static int belt_base_active_element[4] =
5915   {
5916     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5917     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5918     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5919     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5920   };
5921
5922   int x, y, i, j;
5923
5924   // set frame order for belt animation graphic according to belt direction
5925   for (i = 0; i < NUM_BELTS; i++)
5926   {
5927     int belt_nr = i;
5928
5929     for (j = 0; j < NUM_BELT_PARTS; j++)
5930     {
5931       int element = belt_base_active_element[belt_nr] + j;
5932       int graphic_1 = el2img(element);
5933       int graphic_2 = el2panelimg(element);
5934
5935       if (game.belt_dir[i] == MV_LEFT)
5936       {
5937         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5938         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5939       }
5940       else
5941       {
5942         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5943         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5944       }
5945     }
5946   }
5947
5948   SCAN_PLAYFIELD(x, y)
5949   {
5950     int element = Feld[x][y];
5951
5952     for (i = 0; i < NUM_BELTS; i++)
5953     {
5954       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5955       {
5956         int e_belt_nr = getBeltNrFromBeltElement(element);
5957         int belt_nr = i;
5958
5959         if (e_belt_nr == belt_nr)
5960         {
5961           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5962
5963           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5964         }
5965       }
5966     }
5967   }
5968 }
5969
5970 static void ToggleBeltSwitch(int x, int y)
5971 {
5972   static int belt_base_element[4] =
5973   {
5974     EL_CONVEYOR_BELT_1_LEFT,
5975     EL_CONVEYOR_BELT_2_LEFT,
5976     EL_CONVEYOR_BELT_3_LEFT,
5977     EL_CONVEYOR_BELT_4_LEFT
5978   };
5979   static int belt_base_active_element[4] =
5980   {
5981     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5982     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5983     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5984     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5985   };
5986   static int belt_base_switch_element[4] =
5987   {
5988     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5989     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5990     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5991     EL_CONVEYOR_BELT_4_SWITCH_LEFT
5992   };
5993   static int belt_move_dir[4] =
5994   {
5995     MV_LEFT,
5996     MV_NONE,
5997     MV_RIGHT,
5998     MV_NONE,
5999   };
6000
6001   int element = Feld[x][y];
6002   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6003   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6004   int belt_dir = belt_move_dir[belt_dir_nr];
6005   int xx, yy, i;
6006
6007   if (!IS_BELT_SWITCH(element))
6008     return;
6009
6010   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6011   game.belt_dir[belt_nr] = belt_dir;
6012
6013   if (belt_dir_nr == 3)
6014     belt_dir_nr = 1;
6015
6016   // set frame order for belt animation graphic according to belt direction
6017   for (i = 0; i < NUM_BELT_PARTS; i++)
6018   {
6019     int element = belt_base_active_element[belt_nr] + i;
6020     int graphic_1 = el2img(element);
6021     int graphic_2 = el2panelimg(element);
6022
6023     if (belt_dir == MV_LEFT)
6024     {
6025       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6026       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6027     }
6028     else
6029     {
6030       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6031       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6032     }
6033   }
6034
6035   SCAN_PLAYFIELD(xx, yy)
6036   {
6037     int element = Feld[xx][yy];
6038
6039     if (IS_BELT_SWITCH(element))
6040     {
6041       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6042
6043       if (e_belt_nr == belt_nr)
6044       {
6045         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6046         TEST_DrawLevelField(xx, yy);
6047       }
6048     }
6049     else if (IS_BELT(element) && belt_dir != MV_NONE)
6050     {
6051       int e_belt_nr = getBeltNrFromBeltElement(element);
6052
6053       if (e_belt_nr == belt_nr)
6054       {
6055         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
6056
6057         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6058         TEST_DrawLevelField(xx, yy);
6059       }
6060     }
6061     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6062     {
6063       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6064
6065       if (e_belt_nr == belt_nr)
6066       {
6067         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
6068
6069         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
6070         TEST_DrawLevelField(xx, yy);
6071       }
6072     }
6073   }
6074 }
6075
6076 static void ToggleSwitchgateSwitch(int x, int y)
6077 {
6078   int xx, yy;
6079
6080   game.switchgate_pos = !game.switchgate_pos;
6081
6082   SCAN_PLAYFIELD(xx, yy)
6083   {
6084     int element = Feld[xx][yy];
6085
6086     if (element == EL_SWITCHGATE_SWITCH_UP)
6087     {
6088       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6089       TEST_DrawLevelField(xx, yy);
6090     }
6091     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6092     {
6093       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6094       TEST_DrawLevelField(xx, yy);
6095     }
6096     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6097     {
6098       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6099       TEST_DrawLevelField(xx, yy);
6100     }
6101     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6102     {
6103       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6104       TEST_DrawLevelField(xx, yy);
6105     }
6106     else if (element == EL_SWITCHGATE_OPEN ||
6107              element == EL_SWITCHGATE_OPENING)
6108     {
6109       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6110
6111       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6112     }
6113     else if (element == EL_SWITCHGATE_CLOSED ||
6114              element == EL_SWITCHGATE_CLOSING)
6115     {
6116       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6117
6118       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6119     }
6120   }
6121 }
6122
6123 static int getInvisibleActiveFromInvisibleElement(int element)
6124 {
6125   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6126           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6127           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6128           element);
6129 }
6130
6131 static int getInvisibleFromInvisibleActiveElement(int element)
6132 {
6133   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6134           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6135           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6136           element);
6137 }
6138
6139 static void RedrawAllLightSwitchesAndInvisibleElements(void)
6140 {
6141   int x, y;
6142
6143   SCAN_PLAYFIELD(x, y)
6144   {
6145     int element = Feld[x][y];
6146
6147     if (element == EL_LIGHT_SWITCH &&
6148         game.light_time_left > 0)
6149     {
6150       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6151       TEST_DrawLevelField(x, y);
6152     }
6153     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6154              game.light_time_left == 0)
6155     {
6156       Feld[x][y] = EL_LIGHT_SWITCH;
6157       TEST_DrawLevelField(x, y);
6158     }
6159     else if (element == EL_EMC_DRIPPER &&
6160              game.light_time_left > 0)
6161     {
6162       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6163       TEST_DrawLevelField(x, y);
6164     }
6165     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6166              game.light_time_left == 0)
6167     {
6168       Feld[x][y] = EL_EMC_DRIPPER;
6169       TEST_DrawLevelField(x, y);
6170     }
6171     else if (element == EL_INVISIBLE_STEELWALL ||
6172              element == EL_INVISIBLE_WALL ||
6173              element == EL_INVISIBLE_SAND)
6174     {
6175       if (game.light_time_left > 0)
6176         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6177
6178       TEST_DrawLevelField(x, y);
6179
6180       // uncrumble neighbour fields, if needed
6181       if (element == EL_INVISIBLE_SAND)
6182         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6183     }
6184     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6185              element == EL_INVISIBLE_WALL_ACTIVE ||
6186              element == EL_INVISIBLE_SAND_ACTIVE)
6187     {
6188       if (game.light_time_left == 0)
6189         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6190
6191       TEST_DrawLevelField(x, y);
6192
6193       // re-crumble neighbour fields, if needed
6194       if (element == EL_INVISIBLE_SAND)
6195         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6196     }
6197   }
6198 }
6199
6200 static void RedrawAllInvisibleElementsForLenses(void)
6201 {
6202   int x, y;
6203
6204   SCAN_PLAYFIELD(x, y)
6205   {
6206     int element = Feld[x][y];
6207
6208     if (element == EL_EMC_DRIPPER &&
6209         game.lenses_time_left > 0)
6210     {
6211       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6212       TEST_DrawLevelField(x, y);
6213     }
6214     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6215              game.lenses_time_left == 0)
6216     {
6217       Feld[x][y] = EL_EMC_DRIPPER;
6218       TEST_DrawLevelField(x, y);
6219     }
6220     else if (element == EL_INVISIBLE_STEELWALL ||
6221              element == EL_INVISIBLE_WALL ||
6222              element == EL_INVISIBLE_SAND)
6223     {
6224       if (game.lenses_time_left > 0)
6225         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6226
6227       TEST_DrawLevelField(x, y);
6228
6229       // uncrumble neighbour fields, if needed
6230       if (element == EL_INVISIBLE_SAND)
6231         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6232     }
6233     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6234              element == EL_INVISIBLE_WALL_ACTIVE ||
6235              element == EL_INVISIBLE_SAND_ACTIVE)
6236     {
6237       if (game.lenses_time_left == 0)
6238         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6239
6240       TEST_DrawLevelField(x, y);
6241
6242       // re-crumble neighbour fields, if needed
6243       if (element == EL_INVISIBLE_SAND)
6244         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6245     }
6246   }
6247 }
6248
6249 static void RedrawAllInvisibleElementsForMagnifier(void)
6250 {
6251   int x, y;
6252
6253   SCAN_PLAYFIELD(x, y)
6254   {
6255     int element = Feld[x][y];
6256
6257     if (element == EL_EMC_FAKE_GRASS &&
6258         game.magnify_time_left > 0)
6259     {
6260       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6261       TEST_DrawLevelField(x, y);
6262     }
6263     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6264              game.magnify_time_left == 0)
6265     {
6266       Feld[x][y] = EL_EMC_FAKE_GRASS;
6267       TEST_DrawLevelField(x, y);
6268     }
6269     else if (IS_GATE_GRAY(element) &&
6270              game.magnify_time_left > 0)
6271     {
6272       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6273                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6274                     IS_EM_GATE_GRAY(element) ?
6275                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6276                     IS_EMC_GATE_GRAY(element) ?
6277                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6278                     IS_DC_GATE_GRAY(element) ?
6279                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6280                     element);
6281       TEST_DrawLevelField(x, y);
6282     }
6283     else if (IS_GATE_GRAY_ACTIVE(element) &&
6284              game.magnify_time_left == 0)
6285     {
6286       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6287                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6288                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6289                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6290                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6291                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6292                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6293                     EL_DC_GATE_WHITE_GRAY :
6294                     element);
6295       TEST_DrawLevelField(x, y);
6296     }
6297   }
6298 }
6299
6300 static void ToggleLightSwitch(int x, int y)
6301 {
6302   int element = Feld[x][y];
6303
6304   game.light_time_left =
6305     (element == EL_LIGHT_SWITCH ?
6306      level.time_light * FRAMES_PER_SECOND : 0);
6307
6308   RedrawAllLightSwitchesAndInvisibleElements();
6309 }
6310
6311 static void ActivateTimegateSwitch(int x, int y)
6312 {
6313   int xx, yy;
6314
6315   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6316
6317   SCAN_PLAYFIELD(xx, yy)
6318   {
6319     int element = Feld[xx][yy];
6320
6321     if (element == EL_TIMEGATE_CLOSED ||
6322         element == EL_TIMEGATE_CLOSING)
6323     {
6324       Feld[xx][yy] = EL_TIMEGATE_OPENING;
6325       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6326     }
6327
6328     /*
6329     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6330     {
6331       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6332       TEST_DrawLevelField(xx, yy);
6333     }
6334     */
6335
6336   }
6337
6338   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6339                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6340 }
6341
6342 static void Impact(int x, int y)
6343 {
6344   boolean last_line = (y == lev_fieldy - 1);
6345   boolean object_hit = FALSE;
6346   boolean impact = (last_line || object_hit);
6347   int element = Feld[x][y];
6348   int smashed = EL_STEELWALL;
6349
6350   if (!last_line)       // check if element below was hit
6351   {
6352     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6353       return;
6354
6355     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6356                                          MovDir[x][y + 1] != MV_DOWN ||
6357                                          MovPos[x][y + 1] <= TILEY / 2));
6358
6359     // do not smash moving elements that left the smashed field in time
6360     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6361         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6362       object_hit = FALSE;
6363
6364 #if USE_QUICKSAND_IMPACT_BUGFIX
6365     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6366     {
6367       RemoveMovingField(x, y + 1);
6368       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6369       Feld[x][y + 2] = EL_ROCK;
6370       TEST_DrawLevelField(x, y + 2);
6371
6372       object_hit = TRUE;
6373     }
6374
6375     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6376     {
6377       RemoveMovingField(x, y + 1);
6378       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6379       Feld[x][y + 2] = EL_ROCK;
6380       TEST_DrawLevelField(x, y + 2);
6381
6382       object_hit = TRUE;
6383     }
6384 #endif
6385
6386     if (object_hit)
6387       smashed = MovingOrBlocked2Element(x, y + 1);
6388
6389     impact = (last_line || object_hit);
6390   }
6391
6392   if (!last_line && smashed == EL_ACID) // element falls into acid
6393   {
6394     SplashAcid(x, y + 1);
6395     return;
6396   }
6397
6398   // !!! not sufficient for all cases -- see EL_PEARL below !!!
6399   // only reset graphic animation if graphic really changes after impact
6400   if (impact &&
6401       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6402   {
6403     ResetGfxAnimation(x, y);
6404     TEST_DrawLevelField(x, y);
6405   }
6406
6407   if (impact && CAN_EXPLODE_IMPACT(element))
6408   {
6409     Bang(x, y);
6410     return;
6411   }
6412   else if (impact && element == EL_PEARL &&
6413            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6414   {
6415     ResetGfxAnimation(x, y);
6416
6417     Feld[x][y] = EL_PEARL_BREAKING;
6418     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6419     return;
6420   }
6421   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6422   {
6423     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6424
6425     return;
6426   }
6427
6428   if (impact && element == EL_AMOEBA_DROP)
6429   {
6430     if (object_hit && IS_PLAYER(x, y + 1))
6431       KillPlayerUnlessEnemyProtected(x, y + 1);
6432     else if (object_hit && smashed == EL_PENGUIN)
6433       Bang(x, y + 1);
6434     else
6435     {
6436       Feld[x][y] = EL_AMOEBA_GROWING;
6437       Store[x][y] = EL_AMOEBA_WET;
6438
6439       ResetRandomAnimationValue(x, y);
6440     }
6441     return;
6442   }
6443
6444   if (object_hit)               // check which object was hit
6445   {
6446     if ((CAN_PASS_MAGIC_WALL(element) && 
6447          (smashed == EL_MAGIC_WALL ||
6448           smashed == EL_BD_MAGIC_WALL)) ||
6449         (CAN_PASS_DC_MAGIC_WALL(element) &&
6450          smashed == EL_DC_MAGIC_WALL))
6451     {
6452       int xx, yy;
6453       int activated_magic_wall =
6454         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6455          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6456          EL_DC_MAGIC_WALL_ACTIVE);
6457
6458       // activate magic wall / mill
6459       SCAN_PLAYFIELD(xx, yy)
6460       {
6461         if (Feld[xx][yy] == smashed)
6462           Feld[xx][yy] = activated_magic_wall;
6463       }
6464
6465       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6466       game.magic_wall_active = TRUE;
6467
6468       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6469                             SND_MAGIC_WALL_ACTIVATING :
6470                             smashed == EL_BD_MAGIC_WALL ?
6471                             SND_BD_MAGIC_WALL_ACTIVATING :
6472                             SND_DC_MAGIC_WALL_ACTIVATING));
6473     }
6474
6475     if (IS_PLAYER(x, y + 1))
6476     {
6477       if (CAN_SMASH_PLAYER(element))
6478       {
6479         KillPlayerUnlessEnemyProtected(x, y + 1);
6480         return;
6481       }
6482     }
6483     else if (smashed == EL_PENGUIN)
6484     {
6485       if (CAN_SMASH_PLAYER(element))
6486       {
6487         Bang(x, y + 1);
6488         return;
6489       }
6490     }
6491     else if (element == EL_BD_DIAMOND)
6492     {
6493       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6494       {
6495         Bang(x, y + 1);
6496         return;
6497       }
6498     }
6499     else if (((element == EL_SP_INFOTRON ||
6500                element == EL_SP_ZONK) &&
6501               (smashed == EL_SP_SNIKSNAK ||
6502                smashed == EL_SP_ELECTRON ||
6503                smashed == EL_SP_DISK_ORANGE)) ||
6504              (element == EL_SP_INFOTRON &&
6505               smashed == EL_SP_DISK_YELLOW))
6506     {
6507       Bang(x, y + 1);
6508       return;
6509     }
6510     else if (CAN_SMASH_EVERYTHING(element))
6511     {
6512       if (IS_CLASSIC_ENEMY(smashed) ||
6513           CAN_EXPLODE_SMASHED(smashed))
6514       {
6515         Bang(x, y + 1);
6516         return;
6517       }
6518       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6519       {
6520         if (smashed == EL_LAMP ||
6521             smashed == EL_LAMP_ACTIVE)
6522         {
6523           Bang(x, y + 1);
6524           return;
6525         }
6526         else if (smashed == EL_NUT)
6527         {
6528           Feld[x][y + 1] = EL_NUT_BREAKING;
6529           PlayLevelSound(x, y, SND_NUT_BREAKING);
6530           RaiseScoreElement(EL_NUT);
6531           return;
6532         }
6533         else if (smashed == EL_PEARL)
6534         {
6535           ResetGfxAnimation(x, y);
6536
6537           Feld[x][y + 1] = EL_PEARL_BREAKING;
6538           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6539           return;
6540         }
6541         else if (smashed == EL_DIAMOND)
6542         {
6543           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6544           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6545           return;
6546         }
6547         else if (IS_BELT_SWITCH(smashed))
6548         {
6549           ToggleBeltSwitch(x, y + 1);
6550         }
6551         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6552                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6553                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6554                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6555         {
6556           ToggleSwitchgateSwitch(x, y + 1);
6557         }
6558         else if (smashed == EL_LIGHT_SWITCH ||
6559                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6560         {
6561           ToggleLightSwitch(x, y + 1);
6562         }
6563         else
6564         {
6565           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6566
6567           CheckElementChangeBySide(x, y + 1, smashed, element,
6568                                    CE_SWITCHED, CH_SIDE_TOP);
6569           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6570                                             CH_SIDE_TOP);
6571         }
6572       }
6573       else
6574       {
6575         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6576       }
6577     }
6578   }
6579
6580   // play sound of magic wall / mill
6581   if (!last_line &&
6582       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6583        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6584        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6585   {
6586     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6587       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6588     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6589       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6590     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6591       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6592
6593     return;
6594   }
6595
6596   // play sound of object that hits the ground
6597   if (last_line || object_hit)
6598     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6599 }
6600
6601 static void TurnRoundExt(int x, int y)
6602 {
6603   static struct
6604   {
6605     int dx, dy;
6606   } move_xy[] =
6607   {
6608     {  0,  0 },
6609     { -1,  0 },
6610     { +1,  0 },
6611     {  0,  0 },
6612     {  0, -1 },
6613     {  0,  0 }, { 0, 0 }, { 0, 0 },
6614     {  0, +1 }
6615   };
6616   static struct
6617   {
6618     int left, right, back;
6619   } turn[] =
6620   {
6621     { 0,        0,              0        },
6622     { MV_DOWN,  MV_UP,          MV_RIGHT },
6623     { MV_UP,    MV_DOWN,        MV_LEFT  },
6624     { 0,        0,              0        },
6625     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6626     { 0,        0,              0        },
6627     { 0,        0,              0        },
6628     { 0,        0,              0        },
6629     { MV_RIGHT, MV_LEFT,        MV_UP    }
6630   };
6631
6632   int element = Feld[x][y];
6633   int move_pattern = element_info[element].move_pattern;
6634
6635   int old_move_dir = MovDir[x][y];
6636   int left_dir  = turn[old_move_dir].left;
6637   int right_dir = turn[old_move_dir].right;
6638   int back_dir  = turn[old_move_dir].back;
6639
6640   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6641   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6642   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6643   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6644
6645   int left_x  = x + left_dx,  left_y  = y + left_dy;
6646   int right_x = x + right_dx, right_y = y + right_dy;
6647   int move_x  = x + move_dx,  move_y  = y + move_dy;
6648
6649   int xx, yy;
6650
6651   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6652   {
6653     TestIfBadThingTouchesOtherBadThing(x, y);
6654
6655     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6656       MovDir[x][y] = right_dir;
6657     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6658       MovDir[x][y] = left_dir;
6659
6660     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6661       MovDelay[x][y] = 9;
6662     else if (element == EL_BD_BUTTERFLY)     // && MovDir[x][y] == left_dir)
6663       MovDelay[x][y] = 1;
6664   }
6665   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6666   {
6667     TestIfBadThingTouchesOtherBadThing(x, y);
6668
6669     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6670       MovDir[x][y] = left_dir;
6671     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6672       MovDir[x][y] = right_dir;
6673
6674     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6675       MovDelay[x][y] = 9;
6676     else if (element == EL_BD_FIREFLY)      // && MovDir[x][y] == right_dir)
6677       MovDelay[x][y] = 1;
6678   }
6679   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6680   {
6681     TestIfBadThingTouchesOtherBadThing(x, y);
6682
6683     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6684       MovDir[x][y] = left_dir;
6685     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6686       MovDir[x][y] = right_dir;
6687
6688     if (MovDir[x][y] != old_move_dir)
6689       MovDelay[x][y] = 9;
6690   }
6691   else if (element == EL_YAMYAM)
6692   {
6693     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6694     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, 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_DARK_YAMYAM)
6708   {
6709     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6710                                                          left_x, left_y);
6711     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6712                                                          right_x, right_y);
6713
6714     if (can_turn_left && can_turn_right)
6715       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6716     else if (can_turn_left)
6717       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6718     else if (can_turn_right)
6719       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6720     else
6721       MovDir[x][y] = back_dir;
6722
6723     MovDelay[x][y] = 16 + 16 * RND(3);
6724   }
6725   else if (element == EL_PACMAN)
6726   {
6727     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6728     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6729
6730     if (can_turn_left && can_turn_right)
6731       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6732     else if (can_turn_left)
6733       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6734     else if (can_turn_right)
6735       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6736     else
6737       MovDir[x][y] = back_dir;
6738
6739     MovDelay[x][y] = 6 + RND(40);
6740   }
6741   else if (element == EL_PIG)
6742   {
6743     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6744     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6745     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6746     boolean should_turn_left, should_turn_right, should_move_on;
6747     int rnd_value = 24;
6748     int rnd = RND(rnd_value);
6749
6750     should_turn_left = (can_turn_left &&
6751                         (!can_move_on ||
6752                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6753                                                    y + back_dy + left_dy)));
6754     should_turn_right = (can_turn_right &&
6755                          (!can_move_on ||
6756                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6757                                                     y + back_dy + right_dy)));
6758     should_move_on = (can_move_on &&
6759                       (!can_turn_left ||
6760                        !can_turn_right ||
6761                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6762                                                  y + move_dy + left_dy) ||
6763                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6764                                                  y + move_dy + right_dy)));
6765
6766     if (should_turn_left || should_turn_right || should_move_on)
6767     {
6768       if (should_turn_left && should_turn_right && should_move_on)
6769         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6770                         rnd < 2 * rnd_value / 3 ? right_dir :
6771                         old_move_dir);
6772       else if (should_turn_left && should_turn_right)
6773         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6774       else if (should_turn_left && should_move_on)
6775         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6776       else if (should_turn_right && should_move_on)
6777         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6778       else if (should_turn_left)
6779         MovDir[x][y] = left_dir;
6780       else if (should_turn_right)
6781         MovDir[x][y] = right_dir;
6782       else if (should_move_on)
6783         MovDir[x][y] = old_move_dir;
6784     }
6785     else if (can_move_on && rnd > rnd_value / 8)
6786       MovDir[x][y] = old_move_dir;
6787     else if (can_turn_left && can_turn_right)
6788       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6789     else if (can_turn_left && rnd > rnd_value / 8)
6790       MovDir[x][y] = left_dir;
6791     else if (can_turn_right && rnd > rnd_value/8)
6792       MovDir[x][y] = right_dir;
6793     else
6794       MovDir[x][y] = back_dir;
6795
6796     xx = x + move_xy[MovDir[x][y]].dx;
6797     yy = y + move_xy[MovDir[x][y]].dy;
6798
6799     if (!IN_LEV_FIELD(xx, yy) ||
6800         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6801       MovDir[x][y] = old_move_dir;
6802
6803     MovDelay[x][y] = 0;
6804   }
6805   else if (element == EL_DRAGON)
6806   {
6807     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6808     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6809     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6810     int rnd_value = 24;
6811     int rnd = RND(rnd_value);
6812
6813     if (can_move_on && rnd > rnd_value / 8)
6814       MovDir[x][y] = old_move_dir;
6815     else if (can_turn_left && can_turn_right)
6816       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6817     else if (can_turn_left && rnd > rnd_value / 8)
6818       MovDir[x][y] = left_dir;
6819     else if (can_turn_right && rnd > rnd_value / 8)
6820       MovDir[x][y] = right_dir;
6821     else
6822       MovDir[x][y] = back_dir;
6823
6824     xx = x + move_xy[MovDir[x][y]].dx;
6825     yy = y + move_xy[MovDir[x][y]].dy;
6826
6827     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6828       MovDir[x][y] = old_move_dir;
6829
6830     MovDelay[x][y] = 0;
6831   }
6832   else if (element == EL_MOLE)
6833   {
6834     boolean can_move_on =
6835       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6836                             IS_AMOEBOID(Feld[move_x][move_y]) ||
6837                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6838     if (!can_move_on)
6839     {
6840       boolean can_turn_left =
6841         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6842                               IS_AMOEBOID(Feld[left_x][left_y])));
6843
6844       boolean can_turn_right =
6845         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6846                               IS_AMOEBOID(Feld[right_x][right_y])));
6847
6848       if (can_turn_left && can_turn_right)
6849         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6850       else if (can_turn_left)
6851         MovDir[x][y] = left_dir;
6852       else
6853         MovDir[x][y] = right_dir;
6854     }
6855
6856     if (MovDir[x][y] != old_move_dir)
6857       MovDelay[x][y] = 9;
6858   }
6859   else if (element == EL_BALLOON)
6860   {
6861     MovDir[x][y] = game.wind_direction;
6862     MovDelay[x][y] = 0;
6863   }
6864   else if (element == EL_SPRING)
6865   {
6866     if (MovDir[x][y] & MV_HORIZONTAL)
6867     {
6868       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6869           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6870       {
6871         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6872         ResetGfxAnimation(move_x, move_y);
6873         TEST_DrawLevelField(move_x, move_y);
6874
6875         MovDir[x][y] = back_dir;
6876       }
6877       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6878                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6879         MovDir[x][y] = MV_NONE;
6880     }
6881
6882     MovDelay[x][y] = 0;
6883   }
6884   else if (element == EL_ROBOT ||
6885            element == EL_SATELLITE ||
6886            element == EL_PENGUIN ||
6887            element == EL_EMC_ANDROID)
6888   {
6889     int attr_x = -1, attr_y = -1;
6890
6891     if (game.all_players_gone)
6892     {
6893       attr_x = game.exit_x;
6894       attr_y = game.exit_y;
6895     }
6896     else
6897     {
6898       int i;
6899
6900       for (i = 0; i < MAX_PLAYERS; i++)
6901       {
6902         struct PlayerInfo *player = &stored_player[i];
6903         int jx = player->jx, jy = player->jy;
6904
6905         if (!player->active)
6906           continue;
6907
6908         if (attr_x == -1 ||
6909             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6910         {
6911           attr_x = jx;
6912           attr_y = jy;
6913         }
6914       }
6915     }
6916
6917     if (element == EL_ROBOT &&
6918         game.robot_wheel_x >= 0 &&
6919         game.robot_wheel_y >= 0 &&
6920         (Feld[game.robot_wheel_x][game.robot_wheel_y] == EL_ROBOT_WHEEL_ACTIVE ||
6921          game.engine_version < VERSION_IDENT(3,1,0,0)))
6922     {
6923       attr_x = game.robot_wheel_x;
6924       attr_y = game.robot_wheel_y;
6925     }
6926
6927     if (element == EL_PENGUIN)
6928     {
6929       int i;
6930       static int xy[4][2] =
6931       {
6932         { 0, -1 },
6933         { -1, 0 },
6934         { +1, 0 },
6935         { 0, +1 }
6936       };
6937
6938       for (i = 0; i < NUM_DIRECTIONS; i++)
6939       {
6940         int ex = x + xy[i][0];
6941         int ey = y + xy[i][1];
6942
6943         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6944                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6945                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6946                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6947         {
6948           attr_x = ex;
6949           attr_y = ey;
6950           break;
6951         }
6952       }
6953     }
6954
6955     MovDir[x][y] = MV_NONE;
6956     if (attr_x < x)
6957       MovDir[x][y] |= (game.all_players_gone ? MV_RIGHT : MV_LEFT);
6958     else if (attr_x > x)
6959       MovDir[x][y] |= (game.all_players_gone ? MV_LEFT : MV_RIGHT);
6960     if (attr_y < y)
6961       MovDir[x][y] |= (game.all_players_gone ? MV_DOWN : MV_UP);
6962     else if (attr_y > y)
6963       MovDir[x][y] |= (game.all_players_gone ? MV_UP : MV_DOWN);
6964
6965     if (element == EL_ROBOT)
6966     {
6967       int newx, newy;
6968
6969       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6970         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6971       Moving2Blocked(x, y, &newx, &newy);
6972
6973       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6974         MovDelay[x][y] = 8 + 8 * !RND(3);
6975       else
6976         MovDelay[x][y] = 16;
6977     }
6978     else if (element == EL_PENGUIN)
6979     {
6980       int newx, newy;
6981
6982       MovDelay[x][y] = 1;
6983
6984       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6985       {
6986         boolean first_horiz = RND(2);
6987         int new_move_dir = MovDir[x][y];
6988
6989         MovDir[x][y] =
6990           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6991         Moving2Blocked(x, y, &newx, &newy);
6992
6993         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6994           return;
6995
6996         MovDir[x][y] =
6997           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6998         Moving2Blocked(x, y, &newx, &newy);
6999
7000         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7001           return;
7002
7003         MovDir[x][y] = old_move_dir;
7004         return;
7005       }
7006     }
7007     else if (element == EL_SATELLITE)
7008     {
7009       int newx, newy;
7010
7011       MovDelay[x][y] = 1;
7012
7013       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7014       {
7015         boolean first_horiz = RND(2);
7016         int new_move_dir = MovDir[x][y];
7017
7018         MovDir[x][y] =
7019           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7020         Moving2Blocked(x, y, &newx, &newy);
7021
7022         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7023           return;
7024
7025         MovDir[x][y] =
7026           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7027         Moving2Blocked(x, y, &newx, &newy);
7028
7029         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7030           return;
7031
7032         MovDir[x][y] = old_move_dir;
7033         return;
7034       }
7035     }
7036     else if (element == EL_EMC_ANDROID)
7037     {
7038       static int check_pos[16] =
7039       {
7040         -1,             //  0 => (invalid)
7041         7,              //  1 => MV_LEFT
7042         3,              //  2 => MV_RIGHT
7043         -1,             //  3 => (invalid)
7044         1,              //  4 =>            MV_UP
7045         0,              //  5 => MV_LEFT  | MV_UP
7046         2,              //  6 => MV_RIGHT | MV_UP
7047         -1,             //  7 => (invalid)
7048         5,              //  8 =>            MV_DOWN
7049         6,              //  9 => MV_LEFT  | MV_DOWN
7050         4,              // 10 => MV_RIGHT | MV_DOWN
7051         -1,             // 11 => (invalid)
7052         -1,             // 12 => (invalid)
7053         -1,             // 13 => (invalid)
7054         -1,             // 14 => (invalid)
7055         -1,             // 15 => (invalid)
7056       };
7057       static struct
7058       {
7059         int dx, dy;
7060         int dir;
7061       } check_xy[8] =
7062       {
7063         { -1, -1,       MV_LEFT  | MV_UP   },
7064         {  0, -1,                  MV_UP   },
7065         { +1, -1,       MV_RIGHT | MV_UP   },
7066         { +1,  0,       MV_RIGHT           },
7067         { +1, +1,       MV_RIGHT | MV_DOWN },
7068         {  0, +1,                  MV_DOWN },
7069         { -1, +1,       MV_LEFT  | MV_DOWN },
7070         { -1,  0,       MV_LEFT            },
7071       };
7072       int start_pos, check_order;
7073       boolean can_clone = FALSE;
7074       int i;
7075
7076       // check if there is any free field around current position
7077       for (i = 0; i < 8; i++)
7078       {
7079         int newx = x + check_xy[i].dx;
7080         int newy = y + check_xy[i].dy;
7081
7082         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7083         {
7084           can_clone = TRUE;
7085
7086           break;
7087         }
7088       }
7089
7090       if (can_clone)            // randomly find an element to clone
7091       {
7092         can_clone = FALSE;
7093
7094         start_pos = check_pos[RND(8)];
7095         check_order = (RND(2) ? -1 : +1);
7096
7097         for (i = 0; i < 8; i++)
7098         {
7099           int pos_raw = start_pos + i * check_order;
7100           int pos = (pos_raw + 8) % 8;
7101           int newx = x + check_xy[pos].dx;
7102           int newy = y + check_xy[pos].dy;
7103
7104           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7105           {
7106             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7107             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7108
7109             Store[x][y] = Feld[newx][newy];
7110
7111             can_clone = TRUE;
7112
7113             break;
7114           }
7115         }
7116       }
7117
7118       if (can_clone)            // randomly find a direction to move
7119       {
7120         can_clone = FALSE;
7121
7122         start_pos = check_pos[RND(8)];
7123         check_order = (RND(2) ? -1 : +1);
7124
7125         for (i = 0; i < 8; i++)
7126         {
7127           int pos_raw = start_pos + i * check_order;
7128           int pos = (pos_raw + 8) % 8;
7129           int newx = x + check_xy[pos].dx;
7130           int newy = y + check_xy[pos].dy;
7131           int new_move_dir = check_xy[pos].dir;
7132
7133           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7134           {
7135             MovDir[x][y] = new_move_dir;
7136             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7137
7138             can_clone = TRUE;
7139
7140             break;
7141           }
7142         }
7143       }
7144
7145       if (can_clone)            // cloning and moving successful
7146         return;
7147
7148       // cannot clone -- try to move towards player
7149
7150       start_pos = check_pos[MovDir[x][y] & 0x0f];
7151       check_order = (RND(2) ? -1 : +1);
7152
7153       for (i = 0; i < 3; i++)
7154       {
7155         // first check start_pos, then previous/next or (next/previous) pos
7156         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7157         int pos = (pos_raw + 8) % 8;
7158         int newx = x + check_xy[pos].dx;
7159         int newy = y + check_xy[pos].dy;
7160         int new_move_dir = check_xy[pos].dir;
7161
7162         if (IS_PLAYER(newx, newy))
7163           break;
7164
7165         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7166         {
7167           MovDir[x][y] = new_move_dir;
7168           MovDelay[x][y] = level.android_move_time * 8 + 1;
7169
7170           break;
7171         }
7172       }
7173     }
7174   }
7175   else if (move_pattern == MV_TURNING_LEFT ||
7176            move_pattern == MV_TURNING_RIGHT ||
7177            move_pattern == MV_TURNING_LEFT_RIGHT ||
7178            move_pattern == MV_TURNING_RIGHT_LEFT ||
7179            move_pattern == MV_TURNING_RANDOM ||
7180            move_pattern == MV_ALL_DIRECTIONS)
7181   {
7182     boolean can_turn_left =
7183       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7184     boolean can_turn_right =
7185       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7186
7187     if (element_info[element].move_stepsize == 0)       // "not moving"
7188       return;
7189
7190     if (move_pattern == MV_TURNING_LEFT)
7191       MovDir[x][y] = left_dir;
7192     else if (move_pattern == MV_TURNING_RIGHT)
7193       MovDir[x][y] = right_dir;
7194     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7195       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7196     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7197       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7198     else if (move_pattern == MV_TURNING_RANDOM)
7199       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7200                       can_turn_right && !can_turn_left ? right_dir :
7201                       RND(2) ? left_dir : right_dir);
7202     else if (can_turn_left && can_turn_right)
7203       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7204     else if (can_turn_left)
7205       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7206     else if (can_turn_right)
7207       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7208     else
7209       MovDir[x][y] = back_dir;
7210
7211     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7212   }
7213   else if (move_pattern == MV_HORIZONTAL ||
7214            move_pattern == MV_VERTICAL)
7215   {
7216     if (move_pattern & old_move_dir)
7217       MovDir[x][y] = back_dir;
7218     else if (move_pattern == MV_HORIZONTAL)
7219       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7220     else if (move_pattern == MV_VERTICAL)
7221       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7222
7223     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7224   }
7225   else if (move_pattern & MV_ANY_DIRECTION)
7226   {
7227     MovDir[x][y] = move_pattern;
7228     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7229   }
7230   else if (move_pattern & MV_WIND_DIRECTION)
7231   {
7232     MovDir[x][y] = game.wind_direction;
7233     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7234   }
7235   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7236   {
7237     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7238       MovDir[x][y] = left_dir;
7239     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7240       MovDir[x][y] = right_dir;
7241
7242     if (MovDir[x][y] != old_move_dir)
7243       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7244   }
7245   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7246   {
7247     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7248       MovDir[x][y] = right_dir;
7249     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7250       MovDir[x][y] = left_dir;
7251
7252     if (MovDir[x][y] != old_move_dir)
7253       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7254   }
7255   else if (move_pattern == MV_TOWARDS_PLAYER ||
7256            move_pattern == MV_AWAY_FROM_PLAYER)
7257   {
7258     int attr_x = -1, attr_y = -1;
7259     int newx, newy;
7260     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7261
7262     if (game.all_players_gone)
7263     {
7264       attr_x = game.exit_x;
7265       attr_y = game.exit_y;
7266     }
7267     else
7268     {
7269       int i;
7270
7271       for (i = 0; i < MAX_PLAYERS; i++)
7272       {
7273         struct PlayerInfo *player = &stored_player[i];
7274         int jx = player->jx, jy = player->jy;
7275
7276         if (!player->active)
7277           continue;
7278
7279         if (attr_x == -1 ||
7280             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7281         {
7282           attr_x = jx;
7283           attr_y = jy;
7284         }
7285       }
7286     }
7287
7288     MovDir[x][y] = MV_NONE;
7289     if (attr_x < x)
7290       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7291     else if (attr_x > x)
7292       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7293     if (attr_y < y)
7294       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7295     else if (attr_y > y)
7296       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7297
7298     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7299
7300     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7301     {
7302       boolean first_horiz = RND(2);
7303       int new_move_dir = MovDir[x][y];
7304
7305       if (element_info[element].move_stepsize == 0)     // "not moving"
7306       {
7307         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7308         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7309
7310         return;
7311       }
7312
7313       MovDir[x][y] =
7314         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7315       Moving2Blocked(x, y, &newx, &newy);
7316
7317       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7318         return;
7319
7320       MovDir[x][y] =
7321         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7322       Moving2Blocked(x, y, &newx, &newy);
7323
7324       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7325         return;
7326
7327       MovDir[x][y] = old_move_dir;
7328     }
7329   }
7330   else if (move_pattern == MV_WHEN_PUSHED ||
7331            move_pattern == MV_WHEN_DROPPED)
7332   {
7333     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7334       MovDir[x][y] = MV_NONE;
7335
7336     MovDelay[x][y] = 0;
7337   }
7338   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7339   {
7340     static int test_xy[7][2] =
7341     {
7342       { 0, -1 },
7343       { -1, 0 },
7344       { +1, 0 },
7345       { 0, +1 },
7346       { 0, -1 },
7347       { -1, 0 },
7348       { +1, 0 },
7349     };
7350     static int test_dir[7] =
7351     {
7352       MV_UP,
7353       MV_LEFT,
7354       MV_RIGHT,
7355       MV_DOWN,
7356       MV_UP,
7357       MV_LEFT,
7358       MV_RIGHT,
7359     };
7360     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7361     int move_preference = -1000000;     // start with very low preference
7362     int new_move_dir = MV_NONE;
7363     int start_test = RND(4);
7364     int i;
7365
7366     for (i = 0; i < NUM_DIRECTIONS; i++)
7367     {
7368       int move_dir = test_dir[start_test + i];
7369       int move_dir_preference;
7370
7371       xx = x + test_xy[start_test + i][0];
7372       yy = y + test_xy[start_test + i][1];
7373
7374       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7375           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7376       {
7377         new_move_dir = move_dir;
7378
7379         break;
7380       }
7381
7382       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7383         continue;
7384
7385       move_dir_preference = -1 * RunnerVisit[xx][yy];
7386       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7387         move_dir_preference = PlayerVisit[xx][yy];
7388
7389       if (move_dir_preference > move_preference)
7390       {
7391         // prefer field that has not been visited for the longest time
7392         move_preference = move_dir_preference;
7393         new_move_dir = move_dir;
7394       }
7395       else if (move_dir_preference == move_preference &&
7396                move_dir == old_move_dir)
7397       {
7398         // prefer last direction when all directions are preferred equally
7399         move_preference = move_dir_preference;
7400         new_move_dir = move_dir;
7401       }
7402     }
7403
7404     MovDir[x][y] = new_move_dir;
7405     if (old_move_dir != new_move_dir)
7406       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7407   }
7408 }
7409
7410 static void TurnRound(int x, int y)
7411 {
7412   int direction = MovDir[x][y];
7413
7414   TurnRoundExt(x, y);
7415
7416   GfxDir[x][y] = MovDir[x][y];
7417
7418   if (direction != MovDir[x][y])
7419     GfxFrame[x][y] = 0;
7420
7421   if (MovDelay[x][y])
7422     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7423
7424   ResetGfxFrame(x, y);
7425 }
7426
7427 static boolean JustBeingPushed(int x, int y)
7428 {
7429   int i;
7430
7431   for (i = 0; i < MAX_PLAYERS; i++)
7432   {
7433     struct PlayerInfo *player = &stored_player[i];
7434
7435     if (player->active && player->is_pushing && player->MovPos)
7436     {
7437       int next_jx = player->jx + (player->jx - player->last_jx);
7438       int next_jy = player->jy + (player->jy - player->last_jy);
7439
7440       if (x == next_jx && y == next_jy)
7441         return TRUE;
7442     }
7443   }
7444
7445   return FALSE;
7446 }
7447
7448 static void StartMoving(int x, int y)
7449 {
7450   boolean started_moving = FALSE;       // some elements can fall _and_ move
7451   int element = Feld[x][y];
7452
7453   if (Stop[x][y])
7454     return;
7455
7456   if (MovDelay[x][y] == 0)
7457     GfxAction[x][y] = ACTION_DEFAULT;
7458
7459   if (CAN_FALL(element) && y < lev_fieldy - 1)
7460   {
7461     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7462         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7463       if (JustBeingPushed(x, y))
7464         return;
7465
7466     if (element == EL_QUICKSAND_FULL)
7467     {
7468       if (IS_FREE(x, y + 1))
7469       {
7470         InitMovingField(x, y, MV_DOWN);
7471         started_moving = TRUE;
7472
7473         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7474 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7475         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7476           Store[x][y] = EL_ROCK;
7477 #else
7478         Store[x][y] = EL_ROCK;
7479 #endif
7480
7481         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7482       }
7483       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7484       {
7485         if (!MovDelay[x][y])
7486         {
7487           MovDelay[x][y] = TILEY + 1;
7488
7489           ResetGfxAnimation(x, y);
7490           ResetGfxAnimation(x, y + 1);
7491         }
7492
7493         if (MovDelay[x][y])
7494         {
7495           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7496           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7497
7498           MovDelay[x][y]--;
7499           if (MovDelay[x][y])
7500             return;
7501         }
7502
7503         Feld[x][y] = EL_QUICKSAND_EMPTY;
7504         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7505         Store[x][y + 1] = Store[x][y];
7506         Store[x][y] = 0;
7507
7508         PlayLevelSoundAction(x, y, ACTION_FILLING);
7509       }
7510       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7511       {
7512         if (!MovDelay[x][y])
7513         {
7514           MovDelay[x][y] = TILEY + 1;
7515
7516           ResetGfxAnimation(x, y);
7517           ResetGfxAnimation(x, y + 1);
7518         }
7519
7520         if (MovDelay[x][y])
7521         {
7522           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7523           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7524
7525           MovDelay[x][y]--;
7526           if (MovDelay[x][y])
7527             return;
7528         }
7529
7530         Feld[x][y] = EL_QUICKSAND_EMPTY;
7531         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7532         Store[x][y + 1] = Store[x][y];
7533         Store[x][y] = 0;
7534
7535         PlayLevelSoundAction(x, y, ACTION_FILLING);
7536       }
7537     }
7538     else if (element == EL_QUICKSAND_FAST_FULL)
7539     {
7540       if (IS_FREE(x, y + 1))
7541       {
7542         InitMovingField(x, y, MV_DOWN);
7543         started_moving = TRUE;
7544
7545         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7546 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7547         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7548           Store[x][y] = EL_ROCK;
7549 #else
7550         Store[x][y] = EL_ROCK;
7551 #endif
7552
7553         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7554       }
7555       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7556       {
7557         if (!MovDelay[x][y])
7558         {
7559           MovDelay[x][y] = TILEY + 1;
7560
7561           ResetGfxAnimation(x, y);
7562           ResetGfxAnimation(x, y + 1);
7563         }
7564
7565         if (MovDelay[x][y])
7566         {
7567           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7568           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7569
7570           MovDelay[x][y]--;
7571           if (MovDelay[x][y])
7572             return;
7573         }
7574
7575         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7576         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7577         Store[x][y + 1] = Store[x][y];
7578         Store[x][y] = 0;
7579
7580         PlayLevelSoundAction(x, y, ACTION_FILLING);
7581       }
7582       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7583       {
7584         if (!MovDelay[x][y])
7585         {
7586           MovDelay[x][y] = TILEY + 1;
7587
7588           ResetGfxAnimation(x, y);
7589           ResetGfxAnimation(x, y + 1);
7590         }
7591
7592         if (MovDelay[x][y])
7593         {
7594           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7595           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7596
7597           MovDelay[x][y]--;
7598           if (MovDelay[x][y])
7599             return;
7600         }
7601
7602         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7603         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7604         Store[x][y + 1] = Store[x][y];
7605         Store[x][y] = 0;
7606
7607         PlayLevelSoundAction(x, y, ACTION_FILLING);
7608       }
7609     }
7610     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7611              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7612     {
7613       InitMovingField(x, y, MV_DOWN);
7614       started_moving = TRUE;
7615
7616       Feld[x][y] = EL_QUICKSAND_FILLING;
7617       Store[x][y] = element;
7618
7619       PlayLevelSoundAction(x, y, ACTION_FILLING);
7620     }
7621     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7622              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7623     {
7624       InitMovingField(x, y, MV_DOWN);
7625       started_moving = TRUE;
7626
7627       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7628       Store[x][y] = element;
7629
7630       PlayLevelSoundAction(x, y, ACTION_FILLING);
7631     }
7632     else if (element == EL_MAGIC_WALL_FULL)
7633     {
7634       if (IS_FREE(x, y + 1))
7635       {
7636         InitMovingField(x, y, MV_DOWN);
7637         started_moving = TRUE;
7638
7639         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7640         Store[x][y] = EL_CHANGED(Store[x][y]);
7641       }
7642       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7643       {
7644         if (!MovDelay[x][y])
7645           MovDelay[x][y] = TILEY / 4 + 1;
7646
7647         if (MovDelay[x][y])
7648         {
7649           MovDelay[x][y]--;
7650           if (MovDelay[x][y])
7651             return;
7652         }
7653
7654         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7655         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7656         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7657         Store[x][y] = 0;
7658       }
7659     }
7660     else if (element == EL_BD_MAGIC_WALL_FULL)
7661     {
7662       if (IS_FREE(x, y + 1))
7663       {
7664         InitMovingField(x, y, MV_DOWN);
7665         started_moving = TRUE;
7666
7667         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7668         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7669       }
7670       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7671       {
7672         if (!MovDelay[x][y])
7673           MovDelay[x][y] = TILEY / 4 + 1;
7674
7675         if (MovDelay[x][y])
7676         {
7677           MovDelay[x][y]--;
7678           if (MovDelay[x][y])
7679             return;
7680         }
7681
7682         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7683         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7684         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7685         Store[x][y] = 0;
7686       }
7687     }
7688     else if (element == EL_DC_MAGIC_WALL_FULL)
7689     {
7690       if (IS_FREE(x, y + 1))
7691       {
7692         InitMovingField(x, y, MV_DOWN);
7693         started_moving = TRUE;
7694
7695         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7696         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7697       }
7698       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7699       {
7700         if (!MovDelay[x][y])
7701           MovDelay[x][y] = TILEY / 4 + 1;
7702
7703         if (MovDelay[x][y])
7704         {
7705           MovDelay[x][y]--;
7706           if (MovDelay[x][y])
7707             return;
7708         }
7709
7710         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7711         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7712         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7713         Store[x][y] = 0;
7714       }
7715     }
7716     else if ((CAN_PASS_MAGIC_WALL(element) &&
7717               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7718                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7719              (CAN_PASS_DC_MAGIC_WALL(element) &&
7720               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7721
7722     {
7723       InitMovingField(x, y, MV_DOWN);
7724       started_moving = TRUE;
7725
7726       Feld[x][y] =
7727         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7728          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7729          EL_DC_MAGIC_WALL_FILLING);
7730       Store[x][y] = element;
7731     }
7732     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7733     {
7734       SplashAcid(x, y + 1);
7735
7736       InitMovingField(x, y, MV_DOWN);
7737       started_moving = TRUE;
7738
7739       Store[x][y] = EL_ACID;
7740     }
7741     else if (
7742              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7743               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7744              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7745               CAN_FALL(element) && WasJustFalling[x][y] &&
7746               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7747
7748              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7749               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7750               (Feld[x][y + 1] == EL_BLOCKED)))
7751     {
7752       /* this is needed for a special case not covered by calling "Impact()"
7753          from "ContinueMoving()": if an element moves to a tile directly below
7754          another element which was just falling on that tile (which was empty
7755          in the previous frame), the falling element above would just stop
7756          instead of smashing the element below (in previous version, the above
7757          element was just checked for "moving" instead of "falling", resulting
7758          in incorrect smashes caused by horizontal movement of the above
7759          element; also, the case of the player being the element to smash was
7760          simply not covered here... :-/ ) */
7761
7762       CheckCollision[x][y] = 0;
7763       CheckImpact[x][y] = 0;
7764
7765       Impact(x, y);
7766     }
7767     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7768     {
7769       if (MovDir[x][y] == MV_NONE)
7770       {
7771         InitMovingField(x, y, MV_DOWN);
7772         started_moving = TRUE;
7773       }
7774     }
7775     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7776     {
7777       if (WasJustFalling[x][y]) // prevent animation from being restarted
7778         MovDir[x][y] = MV_DOWN;
7779
7780       InitMovingField(x, y, MV_DOWN);
7781       started_moving = TRUE;
7782     }
7783     else if (element == EL_AMOEBA_DROP)
7784     {
7785       Feld[x][y] = EL_AMOEBA_GROWING;
7786       Store[x][y] = EL_AMOEBA_WET;
7787     }
7788     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7789               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7790              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7791              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7792     {
7793       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7794                                 (IS_FREE(x - 1, y + 1) ||
7795                                  Feld[x - 1][y + 1] == EL_ACID));
7796       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7797                                 (IS_FREE(x + 1, y + 1) ||
7798                                  Feld[x + 1][y + 1] == EL_ACID));
7799       boolean can_fall_any  = (can_fall_left || can_fall_right);
7800       boolean can_fall_both = (can_fall_left && can_fall_right);
7801       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7802
7803       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7804       {
7805         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7806           can_fall_right = FALSE;
7807         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7808           can_fall_left = FALSE;
7809         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7810           can_fall_right = FALSE;
7811         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7812           can_fall_left = FALSE;
7813
7814         can_fall_any  = (can_fall_left || can_fall_right);
7815         can_fall_both = FALSE;
7816       }
7817
7818       if (can_fall_both)
7819       {
7820         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7821           can_fall_right = FALSE;       // slip down on left side
7822         else
7823           can_fall_left = !(can_fall_right = RND(2));
7824
7825         can_fall_both = FALSE;
7826       }
7827
7828       if (can_fall_any)
7829       {
7830         // if not determined otherwise, prefer left side for slipping down
7831         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7832         started_moving = TRUE;
7833       }
7834     }
7835     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7836     {
7837       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
7838       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7839       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7840       int belt_dir = game.belt_dir[belt_nr];
7841
7842       if ((belt_dir == MV_LEFT  && left_is_free) ||
7843           (belt_dir == MV_RIGHT && right_is_free))
7844       {
7845         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7846
7847         InitMovingField(x, y, belt_dir);
7848         started_moving = TRUE;
7849
7850         Pushed[x][y] = TRUE;
7851         Pushed[nextx][y] = TRUE;
7852
7853         GfxAction[x][y] = ACTION_DEFAULT;
7854       }
7855       else
7856       {
7857         MovDir[x][y] = 0;       // if element was moving, stop it
7858       }
7859     }
7860   }
7861
7862   // not "else if" because of elements that can fall and move (EL_SPRING)
7863   if (CAN_MOVE(element) && !started_moving)
7864   {
7865     int move_pattern = element_info[element].move_pattern;
7866     int newx, newy;
7867
7868     Moving2Blocked(x, y, &newx, &newy);
7869
7870     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7871       return;
7872
7873     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7874         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7875     {
7876       WasJustMoving[x][y] = 0;
7877       CheckCollision[x][y] = 0;
7878
7879       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7880
7881       if (Feld[x][y] != element)        // element has changed
7882         return;
7883     }
7884
7885     if (!MovDelay[x][y])        // start new movement phase
7886     {
7887       // all objects that can change their move direction after each step
7888       // (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall
7889
7890       if (element != EL_YAMYAM &&
7891           element != EL_DARK_YAMYAM &&
7892           element != EL_PACMAN &&
7893           !(move_pattern & MV_ANY_DIRECTION) &&
7894           move_pattern != MV_TURNING_LEFT &&
7895           move_pattern != MV_TURNING_RIGHT &&
7896           move_pattern != MV_TURNING_LEFT_RIGHT &&
7897           move_pattern != MV_TURNING_RIGHT_LEFT &&
7898           move_pattern != MV_TURNING_RANDOM)
7899       {
7900         TurnRound(x, y);
7901
7902         if (MovDelay[x][y] && (element == EL_BUG ||
7903                                element == EL_SPACESHIP ||
7904                                element == EL_SP_SNIKSNAK ||
7905                                element == EL_SP_ELECTRON ||
7906                                element == EL_MOLE))
7907           TEST_DrawLevelField(x, y);
7908       }
7909     }
7910
7911     if (MovDelay[x][y])         // wait some time before next movement
7912     {
7913       MovDelay[x][y]--;
7914
7915       if (element == EL_ROBOT ||
7916           element == EL_YAMYAM ||
7917           element == EL_DARK_YAMYAM)
7918       {
7919         DrawLevelElementAnimationIfNeeded(x, y, element);
7920         PlayLevelSoundAction(x, y, ACTION_WAITING);
7921       }
7922       else if (element == EL_SP_ELECTRON)
7923         DrawLevelElementAnimationIfNeeded(x, y, element);
7924       else if (element == EL_DRAGON)
7925       {
7926         int i;
7927         int dir = MovDir[x][y];
7928         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7929         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
7930         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
7931                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
7932                        dir == MV_UP     ? IMG_FLAMES_1_UP :
7933                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7934         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7935
7936         GfxAction[x][y] = ACTION_ATTACKING;
7937
7938         if (IS_PLAYER(x, y))
7939           DrawPlayerField(x, y);
7940         else
7941           TEST_DrawLevelField(x, y);
7942
7943         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7944
7945         for (i = 1; i <= 3; i++)
7946         {
7947           int xx = x + i * dx;
7948           int yy = y + i * dy;
7949           int sx = SCREENX(xx);
7950           int sy = SCREENY(yy);
7951           int flame_graphic = graphic + (i - 1);
7952
7953           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7954             break;
7955
7956           if (MovDelay[x][y])
7957           {
7958             int flamed = MovingOrBlocked2Element(xx, yy);
7959
7960             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7961               Bang(xx, yy);
7962             else
7963               RemoveMovingField(xx, yy);
7964
7965             ChangeDelay[xx][yy] = 0;
7966
7967             Feld[xx][yy] = EL_FLAMES;
7968
7969             if (IN_SCR_FIELD(sx, sy))
7970             {
7971               TEST_DrawLevelFieldCrumbled(xx, yy);
7972               DrawGraphic(sx, sy, flame_graphic, frame);
7973             }
7974           }
7975           else
7976           {
7977             if (Feld[xx][yy] == EL_FLAMES)
7978               Feld[xx][yy] = EL_EMPTY;
7979             TEST_DrawLevelField(xx, yy);
7980           }
7981         }
7982       }
7983
7984       if (MovDelay[x][y])       // element still has to wait some time
7985       {
7986         PlayLevelSoundAction(x, y, ACTION_WAITING);
7987
7988         return;
7989       }
7990     }
7991
7992     // now make next step
7993
7994     Moving2Blocked(x, y, &newx, &newy); // get next screen position
7995
7996     if (DONT_COLLIDE_WITH(element) &&
7997         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7998         !PLAYER_ENEMY_PROTECTED(newx, newy))
7999     {
8000       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8001
8002       return;
8003     }
8004
8005     else if (CAN_MOVE_INTO_ACID(element) &&
8006              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
8007              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8008              (MovDir[x][y] == MV_DOWN ||
8009               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8010     {
8011       SplashAcid(newx, newy);
8012       Store[x][y] = EL_ACID;
8013     }
8014     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8015     {
8016       if (Feld[newx][newy] == EL_EXIT_OPEN ||
8017           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
8018           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
8019           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8020       {
8021         RemoveField(x, y);
8022         TEST_DrawLevelField(x, y);
8023
8024         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8025         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8026           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8027
8028         game.friends_still_needed--;
8029         if (!game.friends_still_needed &&
8030             !game.GameOver &&
8031             game.all_players_gone)
8032           LevelSolved();
8033
8034         return;
8035       }
8036       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
8037       {
8038         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8039           TEST_DrawLevelField(newx, newy);
8040         else
8041           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8042       }
8043       else if (!IS_FREE(newx, newy))
8044       {
8045         GfxAction[x][y] = ACTION_WAITING;
8046
8047         if (IS_PLAYER(x, y))
8048           DrawPlayerField(x, y);
8049         else
8050           TEST_DrawLevelField(x, y);
8051
8052         return;
8053       }
8054     }
8055     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8056     {
8057       if (IS_FOOD_PIG(Feld[newx][newy]))
8058       {
8059         if (IS_MOVING(newx, newy))
8060           RemoveMovingField(newx, newy);
8061         else
8062         {
8063           Feld[newx][newy] = EL_EMPTY;
8064           TEST_DrawLevelField(newx, newy);
8065         }
8066
8067         PlayLevelSound(x, y, SND_PIG_DIGGING);
8068       }
8069       else if (!IS_FREE(newx, newy))
8070       {
8071         if (IS_PLAYER(x, y))
8072           DrawPlayerField(x, y);
8073         else
8074           TEST_DrawLevelField(x, y);
8075
8076         return;
8077       }
8078     }
8079     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8080     {
8081       if (Store[x][y] != EL_EMPTY)
8082       {
8083         boolean can_clone = FALSE;
8084         int xx, yy;
8085
8086         // check if element to clone is still there
8087         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8088         {
8089           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8090           {
8091             can_clone = TRUE;
8092
8093             break;
8094           }
8095         }
8096
8097         // cannot clone or target field not free anymore -- do not clone
8098         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8099           Store[x][y] = EL_EMPTY;
8100       }
8101
8102       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8103       {
8104         if (IS_MV_DIAGONAL(MovDir[x][y]))
8105         {
8106           int diagonal_move_dir = MovDir[x][y];
8107           int stored = Store[x][y];
8108           int change_delay = 8;
8109           int graphic;
8110
8111           // android is moving diagonally
8112
8113           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8114
8115           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8116           GfxElement[x][y] = EL_EMC_ANDROID;
8117           GfxAction[x][y] = ACTION_SHRINKING;
8118           GfxDir[x][y] = diagonal_move_dir;
8119           ChangeDelay[x][y] = change_delay;
8120
8121           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8122                                    GfxDir[x][y]);
8123
8124           DrawLevelGraphicAnimation(x, y, graphic);
8125           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8126
8127           if (Feld[newx][newy] == EL_ACID)
8128           {
8129             SplashAcid(newx, newy);
8130
8131             return;
8132           }
8133
8134           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8135
8136           Store[newx][newy] = EL_EMC_ANDROID;
8137           GfxElement[newx][newy] = EL_EMC_ANDROID;
8138           GfxAction[newx][newy] = ACTION_GROWING;
8139           GfxDir[newx][newy] = diagonal_move_dir;
8140           ChangeDelay[newx][newy] = change_delay;
8141
8142           graphic = el_act_dir2img(GfxElement[newx][newy],
8143                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8144
8145           DrawLevelGraphicAnimation(newx, newy, graphic);
8146           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8147
8148           return;
8149         }
8150         else
8151         {
8152           Feld[newx][newy] = EL_EMPTY;
8153           TEST_DrawLevelField(newx, newy);
8154
8155           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8156         }
8157       }
8158       else if (!IS_FREE(newx, newy))
8159       {
8160         return;
8161       }
8162     }
8163     else if (IS_CUSTOM_ELEMENT(element) &&
8164              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8165     {
8166       if (!DigFieldByCE(newx, newy, element))
8167         return;
8168
8169       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8170       {
8171         RunnerVisit[x][y] = FrameCounter;
8172         PlayerVisit[x][y] /= 8;         // expire player visit path
8173       }
8174     }
8175     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8176     {
8177       if (!IS_FREE(newx, newy))
8178       {
8179         if (IS_PLAYER(x, y))
8180           DrawPlayerField(x, y);
8181         else
8182           TEST_DrawLevelField(x, y);
8183
8184         return;
8185       }
8186       else
8187       {
8188         boolean wanna_flame = !RND(10);
8189         int dx = newx - x, dy = newy - y;
8190         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8191         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8192         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8193                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8194         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8195                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8196
8197         if ((wanna_flame ||
8198              IS_CLASSIC_ENEMY(element1) ||
8199              IS_CLASSIC_ENEMY(element2)) &&
8200             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8201             element1 != EL_FLAMES && element2 != EL_FLAMES)
8202         {
8203           ResetGfxAnimation(x, y);
8204           GfxAction[x][y] = ACTION_ATTACKING;
8205
8206           if (IS_PLAYER(x, y))
8207             DrawPlayerField(x, y);
8208           else
8209             TEST_DrawLevelField(x, y);
8210
8211           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8212
8213           MovDelay[x][y] = 50;
8214
8215           Feld[newx][newy] = EL_FLAMES;
8216           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8217             Feld[newx1][newy1] = EL_FLAMES;
8218           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8219             Feld[newx2][newy2] = EL_FLAMES;
8220
8221           return;
8222         }
8223       }
8224     }
8225     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8226              Feld[newx][newy] == EL_DIAMOND)
8227     {
8228       if (IS_MOVING(newx, newy))
8229         RemoveMovingField(newx, newy);
8230       else
8231       {
8232         Feld[newx][newy] = EL_EMPTY;
8233         TEST_DrawLevelField(newx, newy);
8234       }
8235
8236       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8237     }
8238     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8239              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8240     {
8241       if (AmoebaNr[newx][newy])
8242       {
8243         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8244         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8245             Feld[newx][newy] == EL_BD_AMOEBA)
8246           AmoebaCnt[AmoebaNr[newx][newy]]--;
8247       }
8248
8249       if (IS_MOVING(newx, newy))
8250       {
8251         RemoveMovingField(newx, newy);
8252       }
8253       else
8254       {
8255         Feld[newx][newy] = EL_EMPTY;
8256         TEST_DrawLevelField(newx, newy);
8257       }
8258
8259       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8260     }
8261     else if ((element == EL_PACMAN || element == EL_MOLE)
8262              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8263     {
8264       if (AmoebaNr[newx][newy])
8265       {
8266         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8267         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8268             Feld[newx][newy] == EL_BD_AMOEBA)
8269           AmoebaCnt[AmoebaNr[newx][newy]]--;
8270       }
8271
8272       if (element == EL_MOLE)
8273       {
8274         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8275         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8276
8277         ResetGfxAnimation(x, y);
8278         GfxAction[x][y] = ACTION_DIGGING;
8279         TEST_DrawLevelField(x, y);
8280
8281         MovDelay[newx][newy] = 0;       // start amoeba shrinking delay
8282
8283         return;                         // wait for shrinking amoeba
8284       }
8285       else      // element == EL_PACMAN
8286       {
8287         Feld[newx][newy] = EL_EMPTY;
8288         TEST_DrawLevelField(newx, newy);
8289         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8290       }
8291     }
8292     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8293              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8294               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8295     {
8296       // wait for shrinking amoeba to completely disappear
8297       return;
8298     }
8299     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8300     {
8301       // object was running against a wall
8302
8303       TurnRound(x, y);
8304
8305       if (GFX_ELEMENT(element) != EL_SAND)     // !!! FIX THIS (crumble) !!!
8306         DrawLevelElementAnimation(x, y, element);
8307
8308       if (DONT_TOUCH(element))
8309         TestIfBadThingTouchesPlayer(x, y);
8310
8311       return;
8312     }
8313
8314     InitMovingField(x, y, MovDir[x][y]);
8315
8316     PlayLevelSoundAction(x, y, ACTION_MOVING);
8317   }
8318
8319   if (MovDir[x][y])
8320     ContinueMoving(x, y);
8321 }
8322
8323 void ContinueMoving(int x, int y)
8324 {
8325   int element = Feld[x][y];
8326   struct ElementInfo *ei = &element_info[element];
8327   int direction = MovDir[x][y];
8328   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8329   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8330   int newx = x + dx, newy = y + dy;
8331   int stored = Store[x][y];
8332   int stored_new = Store[newx][newy];
8333   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8334   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8335   boolean last_line = (newy == lev_fieldy - 1);
8336
8337   MovPos[x][y] += getElementMoveStepsize(x, y);
8338
8339   if (pushed_by_player) // special case: moving object pushed by player
8340     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8341
8342   if (ABS(MovPos[x][y]) < TILEX)
8343   {
8344     TEST_DrawLevelField(x, y);
8345
8346     return;     // element is still moving
8347   }
8348
8349   // element reached destination field
8350
8351   Feld[x][y] = EL_EMPTY;
8352   Feld[newx][newy] = element;
8353   MovPos[x][y] = 0;     // force "not moving" for "crumbled sand"
8354
8355   if (Store[x][y] == EL_ACID)   // element is moving into acid pool
8356   {
8357     element = Feld[newx][newy] = EL_ACID;
8358   }
8359   else if (element == EL_MOLE)
8360   {
8361     Feld[x][y] = EL_SAND;
8362
8363     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8364   }
8365   else if (element == EL_QUICKSAND_FILLING)
8366   {
8367     element = Feld[newx][newy] = get_next_element(element);
8368     Store[newx][newy] = Store[x][y];
8369   }
8370   else if (element == EL_QUICKSAND_EMPTYING)
8371   {
8372     Feld[x][y] = get_next_element(element);
8373     element = Feld[newx][newy] = Store[x][y];
8374   }
8375   else if (element == EL_QUICKSAND_FAST_FILLING)
8376   {
8377     element = Feld[newx][newy] = get_next_element(element);
8378     Store[newx][newy] = Store[x][y];
8379   }
8380   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8381   {
8382     Feld[x][y] = get_next_element(element);
8383     element = Feld[newx][newy] = Store[x][y];
8384   }
8385   else if (element == EL_MAGIC_WALL_FILLING)
8386   {
8387     element = Feld[newx][newy] = get_next_element(element);
8388     if (!game.magic_wall_active)
8389       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8390     Store[newx][newy] = Store[x][y];
8391   }
8392   else if (element == EL_MAGIC_WALL_EMPTYING)
8393   {
8394     Feld[x][y] = get_next_element(element);
8395     if (!game.magic_wall_active)
8396       Feld[x][y] = EL_MAGIC_WALL_DEAD;
8397     element = Feld[newx][newy] = Store[x][y];
8398
8399     InitField(newx, newy, FALSE);
8400   }
8401   else if (element == EL_BD_MAGIC_WALL_FILLING)
8402   {
8403     element = Feld[newx][newy] = get_next_element(element);
8404     if (!game.magic_wall_active)
8405       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8406     Store[newx][newy] = Store[x][y];
8407   }
8408   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8409   {
8410     Feld[x][y] = get_next_element(element);
8411     if (!game.magic_wall_active)
8412       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8413     element = Feld[newx][newy] = Store[x][y];
8414
8415     InitField(newx, newy, FALSE);
8416   }
8417   else if (element == EL_DC_MAGIC_WALL_FILLING)
8418   {
8419     element = Feld[newx][newy] = get_next_element(element);
8420     if (!game.magic_wall_active)
8421       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8422     Store[newx][newy] = Store[x][y];
8423   }
8424   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8425   {
8426     Feld[x][y] = get_next_element(element);
8427     if (!game.magic_wall_active)
8428       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8429     element = Feld[newx][newy] = Store[x][y];
8430
8431     InitField(newx, newy, FALSE);
8432   }
8433   else if (element == EL_AMOEBA_DROPPING)
8434   {
8435     Feld[x][y] = get_next_element(element);
8436     element = Feld[newx][newy] = Store[x][y];
8437   }
8438   else if (element == EL_SOKOBAN_OBJECT)
8439   {
8440     if (Back[x][y])
8441       Feld[x][y] = Back[x][y];
8442
8443     if (Back[newx][newy])
8444       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8445
8446     Back[x][y] = Back[newx][newy] = 0;
8447   }
8448
8449   Store[x][y] = EL_EMPTY;
8450   MovPos[x][y] = 0;
8451   MovDir[x][y] = 0;
8452   MovDelay[x][y] = 0;
8453
8454   MovDelay[newx][newy] = 0;
8455
8456   if (CAN_CHANGE_OR_HAS_ACTION(element))
8457   {
8458     // copy element change control values to new field
8459     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8460     ChangePage[newx][newy]  = ChangePage[x][y];
8461     ChangeCount[newx][newy] = ChangeCount[x][y];
8462     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8463   }
8464
8465   CustomValue[newx][newy] = CustomValue[x][y];
8466
8467   ChangeDelay[x][y] = 0;
8468   ChangePage[x][y] = -1;
8469   ChangeCount[x][y] = 0;
8470   ChangeEvent[x][y] = -1;
8471
8472   CustomValue[x][y] = 0;
8473
8474   // copy animation control values to new field
8475   GfxFrame[newx][newy]  = GfxFrame[x][y];
8476   GfxRandom[newx][newy] = GfxRandom[x][y];      // keep same random value
8477   GfxAction[newx][newy] = GfxAction[x][y];      // keep action one frame
8478   GfxDir[newx][newy]    = GfxDir[x][y];         // keep element direction
8479
8480   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8481
8482   // some elements can leave other elements behind after moving
8483   if (ei->move_leave_element != EL_EMPTY &&
8484       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8485       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8486   {
8487     int move_leave_element = ei->move_leave_element;
8488
8489     // this makes it possible to leave the removed element again
8490     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8491       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8492
8493     Feld[x][y] = move_leave_element;
8494
8495     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8496       MovDir[x][y] = direction;
8497
8498     InitField(x, y, FALSE);
8499
8500     if (GFX_CRUMBLED(Feld[x][y]))
8501       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8502
8503     if (ELEM_IS_PLAYER(move_leave_element))
8504       RelocatePlayer(x, y, move_leave_element);
8505   }
8506
8507   // do this after checking for left-behind element
8508   ResetGfxAnimation(x, y);      // reset animation values for old field
8509
8510   if (!CAN_MOVE(element) ||
8511       (CAN_FALL(element) && direction == MV_DOWN &&
8512        (element == EL_SPRING ||
8513         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8514         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8515     GfxDir[x][y] = MovDir[newx][newy] = 0;
8516
8517   TEST_DrawLevelField(x, y);
8518   TEST_DrawLevelField(newx, newy);
8519
8520   Stop[newx][newy] = TRUE;      // ignore this element until the next frame
8521
8522   // prevent pushed element from moving on in pushed direction
8523   if (pushed_by_player && CAN_MOVE(element) &&
8524       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8525       !(element_info[element].move_pattern & direction))
8526     TurnRound(newx, newy);
8527
8528   // prevent elements on conveyor belt from moving on in last direction
8529   if (pushed_by_conveyor && CAN_FALL(element) &&
8530       direction & MV_HORIZONTAL)
8531     MovDir[newx][newy] = 0;
8532
8533   if (!pushed_by_player)
8534   {
8535     int nextx = newx + dx, nexty = newy + dy;
8536     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8537
8538     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8539
8540     if (CAN_FALL(element) && direction == MV_DOWN)
8541       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8542
8543     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8544       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8545
8546     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8547       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8548   }
8549
8550   if (DONT_TOUCH(element))      // object may be nasty to player or others
8551   {
8552     TestIfBadThingTouchesPlayer(newx, newy);
8553     TestIfBadThingTouchesFriend(newx, newy);
8554
8555     if (!IS_CUSTOM_ELEMENT(element))
8556       TestIfBadThingTouchesOtherBadThing(newx, newy);
8557   }
8558   else if (element == EL_PENGUIN)
8559     TestIfFriendTouchesBadThing(newx, newy);
8560
8561   if (DONT_GET_HIT_BY(element))
8562   {
8563     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8564   }
8565
8566   // give the player one last chance (one more frame) to move away
8567   if (CAN_FALL(element) && direction == MV_DOWN &&
8568       (last_line || (!IS_FREE(x, newy + 1) &&
8569                      (!IS_PLAYER(x, newy + 1) ||
8570                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8571     Impact(x, newy);
8572
8573   if (pushed_by_player && !game.use_change_when_pushing_bug)
8574   {
8575     int push_side = MV_DIR_OPPOSITE(direction);
8576     struct PlayerInfo *player = PLAYERINFO(x, y);
8577
8578     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8579                                player->index_bit, push_side);
8580     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8581                                         player->index_bit, push_side);
8582   }
8583
8584   if (element == EL_EMC_ANDROID && pushed_by_player)    // make another move
8585     MovDelay[newx][newy] = 1;
8586
8587   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8588
8589   TestIfElementTouchesCustomElement(x, y);      // empty or new element
8590   TestIfElementHitsCustomElement(newx, newy, direction);
8591   TestIfPlayerTouchesCustomElement(newx, newy);
8592   TestIfElementTouchesCustomElement(newx, newy);
8593
8594   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8595       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8596     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8597                              MV_DIR_OPPOSITE(direction));
8598 }
8599
8600 int AmoebeNachbarNr(int ax, int ay)
8601 {
8602   int i;
8603   int element = Feld[ax][ay];
8604   int group_nr = 0;
8605   static int xy[4][2] =
8606   {
8607     { 0, -1 },
8608     { -1, 0 },
8609     { +1, 0 },
8610     { 0, +1 }
8611   };
8612
8613   for (i = 0; i < NUM_DIRECTIONS; i++)
8614   {
8615     int x = ax + xy[i][0];
8616     int y = ay + xy[i][1];
8617
8618     if (!IN_LEV_FIELD(x, y))
8619       continue;
8620
8621     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8622       group_nr = AmoebaNr[x][y];
8623   }
8624
8625   return group_nr;
8626 }
8627
8628 static void AmoebenVereinigen(int ax, int ay)
8629 {
8630   int i, x, y, xx, yy;
8631   int new_group_nr = AmoebaNr[ax][ay];
8632   static int xy[4][2] =
8633   {
8634     { 0, -1 },
8635     { -1, 0 },
8636     { +1, 0 },
8637     { 0, +1 }
8638   };
8639
8640   if (new_group_nr == 0)
8641     return;
8642
8643   for (i = 0; i < NUM_DIRECTIONS; i++)
8644   {
8645     x = ax + xy[i][0];
8646     y = ay + xy[i][1];
8647
8648     if (!IN_LEV_FIELD(x, y))
8649       continue;
8650
8651     if ((Feld[x][y] == EL_AMOEBA_FULL ||
8652          Feld[x][y] == EL_BD_AMOEBA ||
8653          Feld[x][y] == EL_AMOEBA_DEAD) &&
8654         AmoebaNr[x][y] != new_group_nr)
8655     {
8656       int old_group_nr = AmoebaNr[x][y];
8657
8658       if (old_group_nr == 0)
8659         return;
8660
8661       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8662       AmoebaCnt[old_group_nr] = 0;
8663       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8664       AmoebaCnt2[old_group_nr] = 0;
8665
8666       SCAN_PLAYFIELD(xx, yy)
8667       {
8668         if (AmoebaNr[xx][yy] == old_group_nr)
8669           AmoebaNr[xx][yy] = new_group_nr;
8670       }
8671     }
8672   }
8673 }
8674
8675 void AmoebeUmwandeln(int ax, int ay)
8676 {
8677   int i, x, y;
8678
8679   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8680   {
8681     int group_nr = AmoebaNr[ax][ay];
8682
8683 #ifdef DEBUG
8684     if (group_nr == 0)
8685     {
8686       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8687       printf("AmoebeUmwandeln(): This should never happen!\n");
8688       return;
8689     }
8690 #endif
8691
8692     SCAN_PLAYFIELD(x, y)
8693     {
8694       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8695       {
8696         AmoebaNr[x][y] = 0;
8697         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8698       }
8699     }
8700
8701     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8702                             SND_AMOEBA_TURNING_TO_GEM :
8703                             SND_AMOEBA_TURNING_TO_ROCK));
8704     Bang(ax, ay);
8705   }
8706   else
8707   {
8708     static int xy[4][2] =
8709     {
8710       { 0, -1 },
8711       { -1, 0 },
8712       { +1, 0 },
8713       { 0, +1 }
8714     };
8715
8716     for (i = 0; i < NUM_DIRECTIONS; i++)
8717     {
8718       x = ax + xy[i][0];
8719       y = ay + xy[i][1];
8720
8721       if (!IN_LEV_FIELD(x, y))
8722         continue;
8723
8724       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8725       {
8726         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8727                               SND_AMOEBA_TURNING_TO_GEM :
8728                               SND_AMOEBA_TURNING_TO_ROCK));
8729         Bang(x, y);
8730       }
8731     }
8732   }
8733 }
8734
8735 static void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8736 {
8737   int x, y;
8738   int group_nr = AmoebaNr[ax][ay];
8739   boolean done = FALSE;
8740
8741 #ifdef DEBUG
8742   if (group_nr == 0)
8743   {
8744     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8745     printf("AmoebeUmwandelnBD(): This should never happen!\n");
8746     return;
8747   }
8748 #endif
8749
8750   SCAN_PLAYFIELD(x, y)
8751   {
8752     if (AmoebaNr[x][y] == group_nr &&
8753         (Feld[x][y] == EL_AMOEBA_DEAD ||
8754          Feld[x][y] == EL_BD_AMOEBA ||
8755          Feld[x][y] == EL_AMOEBA_GROWING))
8756     {
8757       AmoebaNr[x][y] = 0;
8758       Feld[x][y] = new_element;
8759       InitField(x, y, FALSE);
8760       TEST_DrawLevelField(x, y);
8761       done = TRUE;
8762     }
8763   }
8764
8765   if (done)
8766     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8767                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8768                             SND_BD_AMOEBA_TURNING_TO_GEM));
8769 }
8770
8771 static void AmoebeWaechst(int x, int y)
8772 {
8773   static unsigned int sound_delay = 0;
8774   static unsigned int sound_delay_value = 0;
8775
8776   if (!MovDelay[x][y])          // start new growing cycle
8777   {
8778     MovDelay[x][y] = 7;
8779
8780     if (DelayReached(&sound_delay, sound_delay_value))
8781     {
8782       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8783       sound_delay_value = 30;
8784     }
8785   }
8786
8787   if (MovDelay[x][y])           // wait some time before growing bigger
8788   {
8789     MovDelay[x][y]--;
8790     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8791     {
8792       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8793                                            6 - MovDelay[x][y]);
8794
8795       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8796     }
8797
8798     if (!MovDelay[x][y])
8799     {
8800       Feld[x][y] = Store[x][y];
8801       Store[x][y] = 0;
8802       TEST_DrawLevelField(x, y);
8803     }
8804   }
8805 }
8806
8807 static void AmoebaDisappearing(int x, int y)
8808 {
8809   static unsigned int sound_delay = 0;
8810   static unsigned int sound_delay_value = 0;
8811
8812   if (!MovDelay[x][y])          // start new shrinking cycle
8813   {
8814     MovDelay[x][y] = 7;
8815
8816     if (DelayReached(&sound_delay, sound_delay_value))
8817       sound_delay_value = 30;
8818   }
8819
8820   if (MovDelay[x][y])           // wait some time before shrinking
8821   {
8822     MovDelay[x][y]--;
8823     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8824     {
8825       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8826                                            6 - MovDelay[x][y]);
8827
8828       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8829     }
8830
8831     if (!MovDelay[x][y])
8832     {
8833       Feld[x][y] = EL_EMPTY;
8834       TEST_DrawLevelField(x, y);
8835
8836       // don't let mole enter this field in this cycle;
8837       // (give priority to objects falling to this field from above)
8838       Stop[x][y] = TRUE;
8839     }
8840   }
8841 }
8842
8843 static void AmoebeAbleger(int ax, int ay)
8844 {
8845   int i;
8846   int element = Feld[ax][ay];
8847   int graphic = el2img(element);
8848   int newax = ax, neway = ay;
8849   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8850   static int xy[4][2] =
8851   {
8852     { 0, -1 },
8853     { -1, 0 },
8854     { +1, 0 },
8855     { 0, +1 }
8856   };
8857
8858   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8859   {
8860     Feld[ax][ay] = EL_AMOEBA_DEAD;
8861     TEST_DrawLevelField(ax, ay);
8862     return;
8863   }
8864
8865   if (IS_ANIMATED(graphic))
8866     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8867
8868   if (!MovDelay[ax][ay])        // start making new amoeba field
8869     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8870
8871   if (MovDelay[ax][ay])         // wait some time before making new amoeba
8872   {
8873     MovDelay[ax][ay]--;
8874     if (MovDelay[ax][ay])
8875       return;
8876   }
8877
8878   if (can_drop)                 // EL_AMOEBA_WET or EL_EMC_DRIPPER
8879   {
8880     int start = RND(4);
8881     int x = ax + xy[start][0];
8882     int y = ay + xy[start][1];
8883
8884     if (!IN_LEV_FIELD(x, y))
8885       return;
8886
8887     if (IS_FREE(x, y) ||
8888         CAN_GROW_INTO(Feld[x][y]) ||
8889         Feld[x][y] == EL_QUICKSAND_EMPTY ||
8890         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8891     {
8892       newax = x;
8893       neway = y;
8894     }
8895
8896     if (newax == ax && neway == ay)
8897       return;
8898   }
8899   else                          // normal or "filled" (BD style) amoeba
8900   {
8901     int start = RND(4);
8902     boolean waiting_for_player = FALSE;
8903
8904     for (i = 0; i < NUM_DIRECTIONS; i++)
8905     {
8906       int j = (start + i) % 4;
8907       int x = ax + xy[j][0];
8908       int y = ay + xy[j][1];
8909
8910       if (!IN_LEV_FIELD(x, y))
8911         continue;
8912
8913       if (IS_FREE(x, y) ||
8914           CAN_GROW_INTO(Feld[x][y]) ||
8915           Feld[x][y] == EL_QUICKSAND_EMPTY ||
8916           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8917       {
8918         newax = x;
8919         neway = y;
8920         break;
8921       }
8922       else if (IS_PLAYER(x, y))
8923         waiting_for_player = TRUE;
8924     }
8925
8926     if (newax == ax && neway == ay)             // amoeba cannot grow
8927     {
8928       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8929       {
8930         Feld[ax][ay] = EL_AMOEBA_DEAD;
8931         TEST_DrawLevelField(ax, ay);
8932         AmoebaCnt[AmoebaNr[ax][ay]]--;
8933
8934         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   // amoeba is completely dead
8935         {
8936           if (element == EL_AMOEBA_FULL)
8937             AmoebeUmwandeln(ax, ay);
8938           else if (element == EL_BD_AMOEBA)
8939             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8940         }
8941       }
8942       return;
8943     }
8944     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8945     {
8946       // amoeba gets larger by growing in some direction
8947
8948       int new_group_nr = AmoebaNr[ax][ay];
8949
8950 #ifdef DEBUG
8951   if (new_group_nr == 0)
8952   {
8953     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8954     printf("AmoebeAbleger(): This should never happen!\n");
8955     return;
8956   }
8957 #endif
8958
8959       AmoebaNr[newax][neway] = new_group_nr;
8960       AmoebaCnt[new_group_nr]++;
8961       AmoebaCnt2[new_group_nr]++;
8962
8963       // if amoeba touches other amoeba(s) after growing, unify them
8964       AmoebenVereinigen(newax, neway);
8965
8966       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8967       {
8968         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8969         return;
8970       }
8971     }
8972   }
8973
8974   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8975       (neway == lev_fieldy - 1 && newax != ax))
8976   {
8977     Feld[newax][neway] = EL_AMOEBA_GROWING;     // creation of new amoeba
8978     Store[newax][neway] = element;
8979   }
8980   else if (neway == ay || element == EL_EMC_DRIPPER)
8981   {
8982     Feld[newax][neway] = EL_AMOEBA_DROP;        // drop left/right of amoeba
8983
8984     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8985   }
8986   else
8987   {
8988     InitMovingField(ax, ay, MV_DOWN);           // drop dripping from amoeba
8989     Feld[ax][ay] = EL_AMOEBA_DROPPING;
8990     Store[ax][ay] = EL_AMOEBA_DROP;
8991     ContinueMoving(ax, ay);
8992     return;
8993   }
8994
8995   TEST_DrawLevelField(newax, neway);
8996 }
8997
8998 static void Life(int ax, int ay)
8999 {
9000   int x1, y1, x2, y2;
9001   int life_time = 40;
9002   int element = Feld[ax][ay];
9003   int graphic = el2img(element);
9004   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9005                          level.biomaze);
9006   boolean changed = FALSE;
9007
9008   if (IS_ANIMATED(graphic))
9009     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9010
9011   if (Stop[ax][ay])
9012     return;
9013
9014   if (!MovDelay[ax][ay])        // start new "game of life" cycle
9015     MovDelay[ax][ay] = life_time;
9016
9017   if (MovDelay[ax][ay])         // wait some time before next cycle
9018   {
9019     MovDelay[ax][ay]--;
9020     if (MovDelay[ax][ay])
9021       return;
9022   }
9023
9024   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9025   {
9026     int xx = ax+x1, yy = ay+y1;
9027     int old_element = Feld[xx][yy];
9028     int num_neighbours = 0;
9029
9030     if (!IN_LEV_FIELD(xx, yy))
9031       continue;
9032
9033     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9034     {
9035       int x = xx+x2, y = yy+y2;
9036
9037       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9038         continue;
9039
9040       boolean is_player_cell = (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y));
9041       boolean is_neighbour = FALSE;
9042
9043       if (level.use_life_bugs)
9044         is_neighbour =
9045           (((Feld[x][y] == element || is_player_cell) && !Stop[x][y]) ||
9046            (IS_FREE(x, y)                             &&  Stop[x][y]));
9047       else
9048         is_neighbour =
9049           (Last[x][y] == element || is_player_cell);
9050
9051       if (is_neighbour)
9052         num_neighbours++;
9053     }
9054
9055     boolean is_free = FALSE;
9056
9057     if (level.use_life_bugs)
9058       is_free = (IS_FREE(xx, yy));
9059     else
9060       is_free = (IS_FREE(xx, yy) && Last[xx][yy] == EL_EMPTY);
9061
9062     if (xx == ax && yy == ay)           // field in the middle
9063     {
9064       if (num_neighbours < life_parameter[0] ||
9065           num_neighbours > life_parameter[1])
9066       {
9067         Feld[xx][yy] = EL_EMPTY;
9068         if (Feld[xx][yy] != old_element)
9069           TEST_DrawLevelField(xx, yy);
9070         Stop[xx][yy] = TRUE;
9071         changed = TRUE;
9072       }
9073     }
9074     else if (is_free || CAN_GROW_INTO(Feld[xx][yy]))
9075     {                                   // free border field
9076       if (num_neighbours >= life_parameter[2] &&
9077           num_neighbours <= life_parameter[3])
9078       {
9079         Feld[xx][yy] = element;
9080         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9081         if (Feld[xx][yy] != old_element)
9082           TEST_DrawLevelField(xx, yy);
9083         Stop[xx][yy] = TRUE;
9084         changed = TRUE;
9085       }
9086     }
9087   }
9088
9089   if (changed)
9090     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9091                    SND_GAME_OF_LIFE_GROWING);
9092 }
9093
9094 static void InitRobotWheel(int x, int y)
9095 {
9096   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9097 }
9098
9099 static void RunRobotWheel(int x, int y)
9100 {
9101   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9102 }
9103
9104 static void StopRobotWheel(int x, int y)
9105 {
9106   if (game.robot_wheel_x == x &&
9107       game.robot_wheel_y == y)
9108   {
9109     game.robot_wheel_x = -1;
9110     game.robot_wheel_y = -1;
9111     game.robot_wheel_active = FALSE;
9112   }
9113 }
9114
9115 static void InitTimegateWheel(int x, int y)
9116 {
9117   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9118 }
9119
9120 static void RunTimegateWheel(int x, int y)
9121 {
9122   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9123 }
9124
9125 static void InitMagicBallDelay(int x, int y)
9126 {
9127   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9128 }
9129
9130 static void ActivateMagicBall(int bx, int by)
9131 {
9132   int x, y;
9133
9134   if (level.ball_random)
9135   {
9136     int pos_border = RND(8);    // select one of the eight border elements
9137     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9138     int xx = pos_content % 3;
9139     int yy = pos_content / 3;
9140
9141     x = bx - 1 + xx;
9142     y = by - 1 + yy;
9143
9144     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9145       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9146   }
9147   else
9148   {
9149     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9150     {
9151       int xx = x - bx + 1;
9152       int yy = y - by + 1;
9153
9154       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9155         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9156     }
9157   }
9158
9159   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9160 }
9161
9162 static void CheckExit(int x, int y)
9163 {
9164   if (game.gems_still_needed > 0 ||
9165       game.sokoban_fields_still_needed > 0 ||
9166       game.sokoban_objects_still_needed > 0 ||
9167       game.lights_still_needed > 0)
9168   {
9169     int element = Feld[x][y];
9170     int graphic = el2img(element);
9171
9172     if (IS_ANIMATED(graphic))
9173       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9174
9175     return;
9176   }
9177
9178   // do not re-open exit door closed after last player
9179   if (game.all_players_gone)
9180     return;
9181
9182   Feld[x][y] = EL_EXIT_OPENING;
9183
9184   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9185 }
9186
9187 static void CheckExitEM(int x, int y)
9188 {
9189   if (game.gems_still_needed > 0 ||
9190       game.sokoban_fields_still_needed > 0 ||
9191       game.sokoban_objects_still_needed > 0 ||
9192       game.lights_still_needed > 0)
9193   {
9194     int element = Feld[x][y];
9195     int graphic = el2img(element);
9196
9197     if (IS_ANIMATED(graphic))
9198       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9199
9200     return;
9201   }
9202
9203   // do not re-open exit door closed after last player
9204   if (game.all_players_gone)
9205     return;
9206
9207   Feld[x][y] = EL_EM_EXIT_OPENING;
9208
9209   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9210 }
9211
9212 static void CheckExitSteel(int x, int y)
9213 {
9214   if (game.gems_still_needed > 0 ||
9215       game.sokoban_fields_still_needed > 0 ||
9216       game.sokoban_objects_still_needed > 0 ||
9217       game.lights_still_needed > 0)
9218   {
9219     int element = Feld[x][y];
9220     int graphic = el2img(element);
9221
9222     if (IS_ANIMATED(graphic))
9223       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9224
9225     return;
9226   }
9227
9228   // do not re-open exit door closed after last player
9229   if (game.all_players_gone)
9230     return;
9231
9232   Feld[x][y] = EL_STEEL_EXIT_OPENING;
9233
9234   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9235 }
9236
9237 static void CheckExitSteelEM(int x, int y)
9238 {
9239   if (game.gems_still_needed > 0 ||
9240       game.sokoban_fields_still_needed > 0 ||
9241       game.sokoban_objects_still_needed > 0 ||
9242       game.lights_still_needed > 0)
9243   {
9244     int element = Feld[x][y];
9245     int graphic = el2img(element);
9246
9247     if (IS_ANIMATED(graphic))
9248       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9249
9250     return;
9251   }
9252
9253   // do not re-open exit door closed after last player
9254   if (game.all_players_gone)
9255     return;
9256
9257   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9258
9259   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9260 }
9261
9262 static void CheckExitSP(int x, int y)
9263 {
9264   if (game.gems_still_needed > 0)
9265   {
9266     int element = Feld[x][y];
9267     int graphic = el2img(element);
9268
9269     if (IS_ANIMATED(graphic))
9270       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9271
9272     return;
9273   }
9274
9275   // do not re-open exit door closed after last player
9276   if (game.all_players_gone)
9277     return;
9278
9279   Feld[x][y] = EL_SP_EXIT_OPENING;
9280
9281   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9282 }
9283
9284 static void CloseAllOpenTimegates(void)
9285 {
9286   int x, y;
9287
9288   SCAN_PLAYFIELD(x, y)
9289   {
9290     int element = Feld[x][y];
9291
9292     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9293     {
9294       Feld[x][y] = EL_TIMEGATE_CLOSING;
9295
9296       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9297     }
9298   }
9299 }
9300
9301 static void DrawTwinkleOnField(int x, int y)
9302 {
9303   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9304     return;
9305
9306   if (Feld[x][y] == EL_BD_DIAMOND)
9307     return;
9308
9309   if (MovDelay[x][y] == 0)      // next animation frame
9310     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9311
9312   if (MovDelay[x][y] != 0)      // wait some time before next frame
9313   {
9314     MovDelay[x][y]--;
9315
9316     DrawLevelElementAnimation(x, y, Feld[x][y]);
9317
9318     if (MovDelay[x][y] != 0)
9319     {
9320       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9321                                            10 - MovDelay[x][y]);
9322
9323       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9324     }
9325   }
9326 }
9327
9328 static void MauerWaechst(int x, int y)
9329 {
9330   int delay = 6;
9331
9332   if (!MovDelay[x][y])          // next animation frame
9333     MovDelay[x][y] = 3 * delay;
9334
9335   if (MovDelay[x][y])           // wait some time before next frame
9336   {
9337     MovDelay[x][y]--;
9338
9339     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9340     {
9341       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9342       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9343
9344       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9345     }
9346
9347     if (!MovDelay[x][y])
9348     {
9349       if (MovDir[x][y] == MV_LEFT)
9350       {
9351         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9352           TEST_DrawLevelField(x - 1, y);
9353       }
9354       else if (MovDir[x][y] == MV_RIGHT)
9355       {
9356         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9357           TEST_DrawLevelField(x + 1, y);
9358       }
9359       else if (MovDir[x][y] == MV_UP)
9360       {
9361         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9362           TEST_DrawLevelField(x, y - 1);
9363       }
9364       else
9365       {
9366         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9367           TEST_DrawLevelField(x, y + 1);
9368       }
9369
9370       Feld[x][y] = Store[x][y];
9371       Store[x][y] = 0;
9372       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9373       TEST_DrawLevelField(x, y);
9374     }
9375   }
9376 }
9377
9378 static void MauerAbleger(int ax, int ay)
9379 {
9380   int element = Feld[ax][ay];
9381   int graphic = el2img(element);
9382   boolean oben_frei = FALSE, unten_frei = FALSE;
9383   boolean links_frei = FALSE, rechts_frei = FALSE;
9384   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9385   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9386   boolean new_wall = FALSE;
9387
9388   if (IS_ANIMATED(graphic))
9389     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9390
9391   if (!MovDelay[ax][ay])        // start building new wall
9392     MovDelay[ax][ay] = 6;
9393
9394   if (MovDelay[ax][ay])         // wait some time before building new wall
9395   {
9396     MovDelay[ax][ay]--;
9397     if (MovDelay[ax][ay])
9398       return;
9399   }
9400
9401   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9402     oben_frei = TRUE;
9403   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9404     unten_frei = TRUE;
9405   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9406     links_frei = TRUE;
9407   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9408     rechts_frei = TRUE;
9409
9410   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9411       element == EL_EXPANDABLE_WALL_ANY)
9412   {
9413     if (oben_frei)
9414     {
9415       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9416       Store[ax][ay-1] = element;
9417       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9418       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9419         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9420                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9421       new_wall = TRUE;
9422     }
9423     if (unten_frei)
9424     {
9425       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9426       Store[ax][ay+1] = element;
9427       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9428       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9429         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9430                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9431       new_wall = TRUE;
9432     }
9433   }
9434
9435   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9436       element == EL_EXPANDABLE_WALL_ANY ||
9437       element == EL_EXPANDABLE_WALL ||
9438       element == EL_BD_EXPANDABLE_WALL)
9439   {
9440     if (links_frei)
9441     {
9442       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9443       Store[ax-1][ay] = element;
9444       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9445       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9446         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9447                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9448       new_wall = TRUE;
9449     }
9450
9451     if (rechts_frei)
9452     {
9453       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9454       Store[ax+1][ay] = element;
9455       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9456       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9457         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9458                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9459       new_wall = TRUE;
9460     }
9461   }
9462
9463   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9464     TEST_DrawLevelField(ax, ay);
9465
9466   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9467     oben_massiv = TRUE;
9468   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9469     unten_massiv = TRUE;
9470   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9471     links_massiv = TRUE;
9472   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9473     rechts_massiv = TRUE;
9474
9475   if (((oben_massiv && unten_massiv) ||
9476        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9477        element == EL_EXPANDABLE_WALL) &&
9478       ((links_massiv && rechts_massiv) ||
9479        element == EL_EXPANDABLE_WALL_VERTICAL))
9480     Feld[ax][ay] = EL_WALL;
9481
9482   if (new_wall)
9483     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9484 }
9485
9486 static void MauerAblegerStahl(int ax, int ay)
9487 {
9488   int element = Feld[ax][ay];
9489   int graphic = el2img(element);
9490   boolean oben_frei = FALSE, unten_frei = FALSE;
9491   boolean links_frei = FALSE, rechts_frei = FALSE;
9492   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9493   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9494   boolean new_wall = FALSE;
9495
9496   if (IS_ANIMATED(graphic))
9497     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9498
9499   if (!MovDelay[ax][ay])        // start building new wall
9500     MovDelay[ax][ay] = 6;
9501
9502   if (MovDelay[ax][ay])         // wait some time before building new wall
9503   {
9504     MovDelay[ax][ay]--;
9505     if (MovDelay[ax][ay])
9506       return;
9507   }
9508
9509   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9510     oben_frei = TRUE;
9511   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9512     unten_frei = TRUE;
9513   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9514     links_frei = TRUE;
9515   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9516     rechts_frei = TRUE;
9517
9518   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9519       element == EL_EXPANDABLE_STEELWALL_ANY)
9520   {
9521     if (oben_frei)
9522     {
9523       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9524       Store[ax][ay-1] = element;
9525       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9526       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9527         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9528                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9529       new_wall = TRUE;
9530     }
9531     if (unten_frei)
9532     {
9533       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9534       Store[ax][ay+1] = element;
9535       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9536       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9537         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9538                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9539       new_wall = TRUE;
9540     }
9541   }
9542
9543   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9544       element == EL_EXPANDABLE_STEELWALL_ANY)
9545   {
9546     if (links_frei)
9547     {
9548       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9549       Store[ax-1][ay] = element;
9550       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9551       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9552         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9553                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9554       new_wall = TRUE;
9555     }
9556
9557     if (rechts_frei)
9558     {
9559       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9560       Store[ax+1][ay] = element;
9561       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9562       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9563         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9564                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9565       new_wall = TRUE;
9566     }
9567   }
9568
9569   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9570     oben_massiv = TRUE;
9571   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9572     unten_massiv = TRUE;
9573   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9574     links_massiv = TRUE;
9575   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9576     rechts_massiv = TRUE;
9577
9578   if (((oben_massiv && unten_massiv) ||
9579        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9580       ((links_massiv && rechts_massiv) ||
9581        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9582     Feld[ax][ay] = EL_STEELWALL;
9583
9584   if (new_wall)
9585     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9586 }
9587
9588 static void CheckForDragon(int x, int y)
9589 {
9590   int i, j;
9591   boolean dragon_found = FALSE;
9592   static int xy[4][2] =
9593   {
9594     { 0, -1 },
9595     { -1, 0 },
9596     { +1, 0 },
9597     { 0, +1 }
9598   };
9599
9600   for (i = 0; i < NUM_DIRECTIONS; i++)
9601   {
9602     for (j = 0; j < 4; j++)
9603     {
9604       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9605
9606       if (IN_LEV_FIELD(xx, yy) &&
9607           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9608       {
9609         if (Feld[xx][yy] == EL_DRAGON)
9610           dragon_found = TRUE;
9611       }
9612       else
9613         break;
9614     }
9615   }
9616
9617   if (!dragon_found)
9618   {
9619     for (i = 0; i < NUM_DIRECTIONS; i++)
9620     {
9621       for (j = 0; j < 3; j++)
9622       {
9623         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9624   
9625         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9626         {
9627           Feld[xx][yy] = EL_EMPTY;
9628           TEST_DrawLevelField(xx, yy);
9629         }
9630         else
9631           break;
9632       }
9633     }
9634   }
9635 }
9636
9637 static void InitBuggyBase(int x, int y)
9638 {
9639   int element = Feld[x][y];
9640   int activating_delay = FRAMES_PER_SECOND / 4;
9641
9642   ChangeDelay[x][y] =
9643     (element == EL_SP_BUGGY_BASE ?
9644      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9645      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9646      activating_delay :
9647      element == EL_SP_BUGGY_BASE_ACTIVE ?
9648      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9649 }
9650
9651 static void WarnBuggyBase(int x, int y)
9652 {
9653   int i;
9654   static int xy[4][2] =
9655   {
9656     { 0, -1 },
9657     { -1, 0 },
9658     { +1, 0 },
9659     { 0, +1 }
9660   };
9661
9662   for (i = 0; i < NUM_DIRECTIONS; i++)
9663   {
9664     int xx = x + xy[i][0];
9665     int yy = y + xy[i][1];
9666
9667     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9668     {
9669       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9670
9671       break;
9672     }
9673   }
9674 }
9675
9676 static void InitTrap(int x, int y)
9677 {
9678   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9679 }
9680
9681 static void ActivateTrap(int x, int y)
9682 {
9683   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9684 }
9685
9686 static void ChangeActiveTrap(int x, int y)
9687 {
9688   int graphic = IMG_TRAP_ACTIVE;
9689
9690   // if new animation frame was drawn, correct crumbled sand border
9691   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9692     TEST_DrawLevelFieldCrumbled(x, y);
9693 }
9694
9695 static int getSpecialActionElement(int element, int number, int base_element)
9696 {
9697   return (element != EL_EMPTY ? element :
9698           number != -1 ? base_element + number - 1 :
9699           EL_EMPTY);
9700 }
9701
9702 static int getModifiedActionNumber(int value_old, int operator, int operand,
9703                                    int value_min, int value_max)
9704 {
9705   int value_new = (operator == CA_MODE_SET      ? operand :
9706                    operator == CA_MODE_ADD      ? value_old + operand :
9707                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9708                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9709                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9710                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9711                    value_old);
9712
9713   return (value_new < value_min ? value_min :
9714           value_new > value_max ? value_max :
9715           value_new);
9716 }
9717
9718 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9719 {
9720   struct ElementInfo *ei = &element_info[element];
9721   struct ElementChangeInfo *change = &ei->change_page[page];
9722   int target_element = change->target_element;
9723   int action_type = change->action_type;
9724   int action_mode = change->action_mode;
9725   int action_arg = change->action_arg;
9726   int action_element = change->action_element;
9727   int i;
9728
9729   if (!change->has_action)
9730     return;
9731
9732   // ---------- determine action paramater values -----------------------------
9733
9734   int level_time_value =
9735     (level.time > 0 ? TimeLeft :
9736      TimePlayed);
9737
9738   int action_arg_element_raw =
9739     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9740      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9741      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9742      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
9743      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9744      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
9745      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
9746      EL_EMPTY);
9747   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9748
9749   int action_arg_direction =
9750     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9751      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9752      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9753      change->actual_trigger_side :
9754      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9755      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9756      MV_NONE);
9757
9758   int action_arg_number_min =
9759     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9760      CA_ARG_MIN);
9761
9762   int action_arg_number_max =
9763     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9764      action_type == CA_SET_LEVEL_GEMS ? 999 :
9765      action_type == CA_SET_LEVEL_TIME ? 9999 :
9766      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9767      action_type == CA_SET_CE_VALUE ? 9999 :
9768      action_type == CA_SET_CE_SCORE ? 9999 :
9769      CA_ARG_MAX);
9770
9771   int action_arg_number_reset =
9772     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9773      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9774      action_type == CA_SET_LEVEL_TIME ? level.time :
9775      action_type == CA_SET_LEVEL_SCORE ? 0 :
9776      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9777      action_type == CA_SET_CE_SCORE ? 0 :
9778      0);
9779
9780   int action_arg_number =
9781     (action_arg <= CA_ARG_MAX ? action_arg :
9782      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9783      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9784      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9785      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9786      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9787      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9788      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9789      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9790      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9791      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9792      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? game.gems_still_needed :
9793      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? game.score :
9794      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9795      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9796      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9797      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9798      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9799      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9800      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
9801      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9802      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
9803      -1);
9804
9805   int action_arg_number_old =
9806     (action_type == CA_SET_LEVEL_GEMS ? game.gems_still_needed :
9807      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9808      action_type == CA_SET_LEVEL_SCORE ? game.score :
9809      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9810      action_type == CA_SET_CE_SCORE ? ei->collect_score :
9811      0);
9812
9813   int action_arg_number_new =
9814     getModifiedActionNumber(action_arg_number_old,
9815                             action_mode, action_arg_number,
9816                             action_arg_number_min, action_arg_number_max);
9817
9818   int trigger_player_bits =
9819     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9820      change->actual_trigger_player_bits : change->trigger_player);
9821
9822   int action_arg_player_bits =
9823     (action_arg >= CA_ARG_PLAYER_1 &&
9824      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9825      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9826      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9827      PLAYER_BITS_ANY);
9828
9829   // ---------- execute action  -----------------------------------------------
9830
9831   switch (action_type)
9832   {
9833     case CA_NO_ACTION:
9834     {
9835       return;
9836     }
9837
9838     // ---------- level actions  ----------------------------------------------
9839
9840     case CA_RESTART_LEVEL:
9841     {
9842       game.restart_level = TRUE;
9843
9844       break;
9845     }
9846
9847     case CA_SHOW_ENVELOPE:
9848     {
9849       int element = getSpecialActionElement(action_arg_element,
9850                                             action_arg_number, EL_ENVELOPE_1);
9851
9852       if (IS_ENVELOPE(element))
9853         local_player->show_envelope = element;
9854
9855       break;
9856     }
9857
9858     case CA_SET_LEVEL_TIME:
9859     {
9860       if (level.time > 0)       // only modify limited time value
9861       {
9862         TimeLeft = action_arg_number_new;
9863
9864         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9865
9866         DisplayGameControlValues();
9867
9868         if (!TimeLeft && setup.time_limit)
9869           for (i = 0; i < MAX_PLAYERS; i++)
9870             KillPlayer(&stored_player[i]);
9871       }
9872
9873       break;
9874     }
9875
9876     case CA_SET_LEVEL_SCORE:
9877     {
9878       game.score = action_arg_number_new;
9879
9880       game_panel_controls[GAME_PANEL_SCORE].value = game.score;
9881
9882       DisplayGameControlValues();
9883
9884       break;
9885     }
9886
9887     case CA_SET_LEVEL_GEMS:
9888     {
9889       game.gems_still_needed = action_arg_number_new;
9890
9891       game.snapshot.collected_item = TRUE;
9892
9893       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
9894
9895       DisplayGameControlValues();
9896
9897       break;
9898     }
9899
9900     case CA_SET_LEVEL_WIND:
9901     {
9902       game.wind_direction = action_arg_direction;
9903
9904       break;
9905     }
9906
9907     case CA_SET_LEVEL_RANDOM_SEED:
9908     {
9909       // ensure that setting a new random seed while playing is predictable
9910       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
9911
9912       break;
9913     }
9914
9915     // ---------- player actions  ---------------------------------------------
9916
9917     case CA_MOVE_PLAYER:
9918     case CA_MOVE_PLAYER_NEW:
9919     {
9920       // automatically move to the next field in specified direction
9921       for (i = 0; i < MAX_PLAYERS; i++)
9922         if (trigger_player_bits & (1 << i))
9923           if (action_type == CA_MOVE_PLAYER ||
9924               stored_player[i].MovPos == 0)
9925             stored_player[i].programmed_action = action_arg_direction;
9926
9927       break;
9928     }
9929
9930     case CA_EXIT_PLAYER:
9931     {
9932       for (i = 0; i < MAX_PLAYERS; i++)
9933         if (action_arg_player_bits & (1 << i))
9934           ExitPlayer(&stored_player[i]);
9935
9936       if (game.players_still_needed == 0)
9937         LevelSolved();
9938
9939       break;
9940     }
9941
9942     case CA_KILL_PLAYER:
9943     {
9944       for (i = 0; i < MAX_PLAYERS; i++)
9945         if (action_arg_player_bits & (1 << i))
9946           KillPlayer(&stored_player[i]);
9947
9948       break;
9949     }
9950
9951     case CA_SET_PLAYER_KEYS:
9952     {
9953       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9954       int element = getSpecialActionElement(action_arg_element,
9955                                             action_arg_number, EL_KEY_1);
9956
9957       if (IS_KEY(element))
9958       {
9959         for (i = 0; i < MAX_PLAYERS; i++)
9960         {
9961           if (trigger_player_bits & (1 << i))
9962           {
9963             stored_player[i].key[KEY_NR(element)] = key_state;
9964
9965             DrawGameDoorValues();
9966           }
9967         }
9968       }
9969
9970       break;
9971     }
9972
9973     case CA_SET_PLAYER_SPEED:
9974     {
9975       for (i = 0; i < MAX_PLAYERS; i++)
9976       {
9977         if (trigger_player_bits & (1 << i))
9978         {
9979           int move_stepsize = TILEX / stored_player[i].move_delay_value;
9980
9981           if (action_arg == CA_ARG_SPEED_FASTER &&
9982               stored_player[i].cannot_move)
9983           {
9984             action_arg_number = STEPSIZE_VERY_SLOW;
9985           }
9986           else if (action_arg == CA_ARG_SPEED_SLOWER ||
9987                    action_arg == CA_ARG_SPEED_FASTER)
9988           {
9989             action_arg_number = 2;
9990             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9991                            CA_MODE_MULTIPLY);
9992           }
9993           else if (action_arg == CA_ARG_NUMBER_RESET)
9994           {
9995             action_arg_number = level.initial_player_stepsize[i];
9996           }
9997
9998           move_stepsize =
9999             getModifiedActionNumber(move_stepsize,
10000                                     action_mode,
10001                                     action_arg_number,
10002                                     action_arg_number_min,
10003                                     action_arg_number_max);
10004
10005           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10006         }
10007       }
10008
10009       break;
10010     }
10011
10012     case CA_SET_PLAYER_SHIELD:
10013     {
10014       for (i = 0; i < MAX_PLAYERS; i++)
10015       {
10016         if (trigger_player_bits & (1 << i))
10017         {
10018           if (action_arg == CA_ARG_SHIELD_OFF)
10019           {
10020             stored_player[i].shield_normal_time_left = 0;
10021             stored_player[i].shield_deadly_time_left = 0;
10022           }
10023           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10024           {
10025             stored_player[i].shield_normal_time_left = 999999;
10026           }
10027           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10028           {
10029             stored_player[i].shield_normal_time_left = 999999;
10030             stored_player[i].shield_deadly_time_left = 999999;
10031           }
10032         }
10033       }
10034
10035       break;
10036     }
10037
10038     case CA_SET_PLAYER_GRAVITY:
10039     {
10040       for (i = 0; i < MAX_PLAYERS; i++)
10041       {
10042         if (trigger_player_bits & (1 << i))
10043         {
10044           stored_player[i].gravity =
10045             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10046              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10047              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10048              stored_player[i].gravity);
10049         }
10050       }
10051
10052       break;
10053     }
10054
10055     case CA_SET_PLAYER_ARTWORK:
10056     {
10057       for (i = 0; i < MAX_PLAYERS; i++)
10058       {
10059         if (trigger_player_bits & (1 << i))
10060         {
10061           int artwork_element = action_arg_element;
10062
10063           if (action_arg == CA_ARG_ELEMENT_RESET)
10064             artwork_element =
10065               (level.use_artwork_element[i] ? level.artwork_element[i] :
10066                stored_player[i].element_nr);
10067
10068           if (stored_player[i].artwork_element != artwork_element)
10069             stored_player[i].Frame = 0;
10070
10071           stored_player[i].artwork_element = artwork_element;
10072
10073           SetPlayerWaiting(&stored_player[i], FALSE);
10074
10075           // set number of special actions for bored and sleeping animation
10076           stored_player[i].num_special_action_bored =
10077             get_num_special_action(artwork_element,
10078                                    ACTION_BORING_1, ACTION_BORING_LAST);
10079           stored_player[i].num_special_action_sleeping =
10080             get_num_special_action(artwork_element,
10081                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10082         }
10083       }
10084
10085       break;
10086     }
10087
10088     case CA_SET_PLAYER_INVENTORY:
10089     {
10090       for (i = 0; i < MAX_PLAYERS; i++)
10091       {
10092         struct PlayerInfo *player = &stored_player[i];
10093         int j, k;
10094
10095         if (trigger_player_bits & (1 << i))
10096         {
10097           int inventory_element = action_arg_element;
10098
10099           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10100               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10101               action_arg == CA_ARG_ELEMENT_ACTION)
10102           {
10103             int element = inventory_element;
10104             int collect_count = element_info[element].collect_count_initial;
10105
10106             if (!IS_CUSTOM_ELEMENT(element))
10107               collect_count = 1;
10108
10109             if (collect_count == 0)
10110               player->inventory_infinite_element = element;
10111             else
10112               for (k = 0; k < collect_count; k++)
10113                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10114                   player->inventory_element[player->inventory_size++] =
10115                     element;
10116           }
10117           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10118                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10119                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10120           {
10121             if (player->inventory_infinite_element != EL_UNDEFINED &&
10122                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10123                                      action_arg_element_raw))
10124               player->inventory_infinite_element = EL_UNDEFINED;
10125
10126             for (k = 0, j = 0; j < player->inventory_size; j++)
10127             {
10128               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10129                                         action_arg_element_raw))
10130                 player->inventory_element[k++] = player->inventory_element[j];
10131             }
10132
10133             player->inventory_size = k;
10134           }
10135           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10136           {
10137             if (player->inventory_size > 0)
10138             {
10139               for (j = 0; j < player->inventory_size - 1; j++)
10140                 player->inventory_element[j] = player->inventory_element[j + 1];
10141
10142               player->inventory_size--;
10143             }
10144           }
10145           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10146           {
10147             if (player->inventory_size > 0)
10148               player->inventory_size--;
10149           }
10150           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10151           {
10152             player->inventory_infinite_element = EL_UNDEFINED;
10153             player->inventory_size = 0;
10154           }
10155           else if (action_arg == CA_ARG_INVENTORY_RESET)
10156           {
10157             player->inventory_infinite_element = EL_UNDEFINED;
10158             player->inventory_size = 0;
10159
10160             if (level.use_initial_inventory[i])
10161             {
10162               for (j = 0; j < level.initial_inventory_size[i]; j++)
10163               {
10164                 int element = level.initial_inventory_content[i][j];
10165                 int collect_count = element_info[element].collect_count_initial;
10166
10167                 if (!IS_CUSTOM_ELEMENT(element))
10168                   collect_count = 1;
10169
10170                 if (collect_count == 0)
10171                   player->inventory_infinite_element = element;
10172                 else
10173                   for (k = 0; k < collect_count; k++)
10174                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10175                       player->inventory_element[player->inventory_size++] =
10176                         element;
10177               }
10178             }
10179           }
10180         }
10181       }
10182
10183       break;
10184     }
10185
10186     // ---------- CE actions  -------------------------------------------------
10187
10188     case CA_SET_CE_VALUE:
10189     {
10190       int last_ce_value = CustomValue[x][y];
10191
10192       CustomValue[x][y] = action_arg_number_new;
10193
10194       if (CustomValue[x][y] != last_ce_value)
10195       {
10196         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10197         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10198
10199         if (CustomValue[x][y] == 0)
10200         {
10201           // reset change counter (else CE_VALUE_GETS_ZERO would not work)
10202           ChangeCount[x][y] = 0;        // allow at least one more change
10203
10204           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10205           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10206         }
10207       }
10208
10209       break;
10210     }
10211
10212     case CA_SET_CE_SCORE:
10213     {
10214       int last_ce_score = ei->collect_score;
10215
10216       ei->collect_score = action_arg_number_new;
10217
10218       if (ei->collect_score != last_ce_score)
10219       {
10220         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10221         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10222
10223         if (ei->collect_score == 0)
10224         {
10225           int xx, yy;
10226
10227           // reset change counter (else CE_SCORE_GETS_ZERO would not work)
10228           ChangeCount[x][y] = 0;        // allow at least one more change
10229
10230           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10231           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10232
10233           /*
10234             This is a very special case that seems to be a mixture between
10235             CheckElementChange() and CheckTriggeredElementChange(): while
10236             the first one only affects single elements that are triggered
10237             directly, the second one affects multiple elements in the playfield
10238             that are triggered indirectly by another element. This is a third
10239             case: Changing the CE score always affects multiple identical CEs,
10240             so every affected CE must be checked, not only the single CE for
10241             which the CE score was changed in the first place (as every instance
10242             of that CE shares the same CE score, and therefore also can change)!
10243           */
10244           SCAN_PLAYFIELD(xx, yy)
10245           {
10246             if (Feld[xx][yy] == element)
10247               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10248                                  CE_SCORE_GETS_ZERO);
10249           }
10250         }
10251       }
10252
10253       break;
10254     }
10255
10256     case CA_SET_CE_ARTWORK:
10257     {
10258       int artwork_element = action_arg_element;
10259       boolean reset_frame = FALSE;
10260       int xx, yy;
10261
10262       if (action_arg == CA_ARG_ELEMENT_RESET)
10263         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10264                            element);
10265
10266       if (ei->gfx_element != artwork_element)
10267         reset_frame = TRUE;
10268
10269       ei->gfx_element = artwork_element;
10270
10271       SCAN_PLAYFIELD(xx, yy)
10272       {
10273         if (Feld[xx][yy] == element)
10274         {
10275           if (reset_frame)
10276           {
10277             ResetGfxAnimation(xx, yy);
10278             ResetRandomAnimationValue(xx, yy);
10279           }
10280
10281           TEST_DrawLevelField(xx, yy);
10282         }
10283       }
10284
10285       break;
10286     }
10287
10288     // ---------- engine actions  ---------------------------------------------
10289
10290     case CA_SET_ENGINE_SCAN_MODE:
10291     {
10292       InitPlayfieldScanMode(action_arg);
10293
10294       break;
10295     }
10296
10297     default:
10298       break;
10299   }
10300 }
10301
10302 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10303 {
10304   int old_element = Feld[x][y];
10305   int new_element = GetElementFromGroupElement(element);
10306   int previous_move_direction = MovDir[x][y];
10307   int last_ce_value = CustomValue[x][y];
10308   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10309   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10310   boolean add_player_onto_element = (new_element_is_player &&
10311                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10312                                      IS_WALKABLE(old_element));
10313
10314   if (!add_player_onto_element)
10315   {
10316     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10317       RemoveMovingField(x, y);
10318     else
10319       RemoveField(x, y);
10320
10321     Feld[x][y] = new_element;
10322
10323     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10324       MovDir[x][y] = previous_move_direction;
10325
10326     if (element_info[new_element].use_last_ce_value)
10327       CustomValue[x][y] = last_ce_value;
10328
10329     InitField_WithBug1(x, y, FALSE);
10330
10331     new_element = Feld[x][y];   // element may have changed
10332
10333     ResetGfxAnimation(x, y);
10334     ResetRandomAnimationValue(x, y);
10335
10336     TEST_DrawLevelField(x, y);
10337
10338     if (GFX_CRUMBLED(new_element))
10339       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10340   }
10341
10342   // check if element under the player changes from accessible to unaccessible
10343   // (needed for special case of dropping element which then changes)
10344   // (must be checked after creating new element for walkable group elements)
10345   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10346       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10347   {
10348     Bang(x, y);
10349
10350     return;
10351   }
10352
10353   // "ChangeCount" not set yet to allow "entered by player" change one time
10354   if (new_element_is_player)
10355     RelocatePlayer(x, y, new_element);
10356
10357   if (is_change)
10358     ChangeCount[x][y]++;        // count number of changes in the same frame
10359
10360   TestIfBadThingTouchesPlayer(x, y);
10361   TestIfPlayerTouchesCustomElement(x, y);
10362   TestIfElementTouchesCustomElement(x, y);
10363 }
10364
10365 static void CreateField(int x, int y, int element)
10366 {
10367   CreateFieldExt(x, y, element, FALSE);
10368 }
10369
10370 static void CreateElementFromChange(int x, int y, int element)
10371 {
10372   element = GET_VALID_RUNTIME_ELEMENT(element);
10373
10374   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10375   {
10376     int old_element = Feld[x][y];
10377
10378     // prevent changed element from moving in same engine frame
10379     // unless both old and new element can either fall or move
10380     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10381         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10382       Stop[x][y] = TRUE;
10383   }
10384
10385   CreateFieldExt(x, y, element, TRUE);
10386 }
10387
10388 static boolean ChangeElement(int x, int y, int element, int page)
10389 {
10390   struct ElementInfo *ei = &element_info[element];
10391   struct ElementChangeInfo *change = &ei->change_page[page];
10392   int ce_value = CustomValue[x][y];
10393   int ce_score = ei->collect_score;
10394   int target_element;
10395   int old_element = Feld[x][y];
10396
10397   // always use default change event to prevent running into a loop
10398   if (ChangeEvent[x][y] == -1)
10399     ChangeEvent[x][y] = CE_DELAY;
10400
10401   if (ChangeEvent[x][y] == CE_DELAY)
10402   {
10403     // reset actual trigger element, trigger player and action element
10404     change->actual_trigger_element = EL_EMPTY;
10405     change->actual_trigger_player = EL_EMPTY;
10406     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10407     change->actual_trigger_side = CH_SIDE_NONE;
10408     change->actual_trigger_ce_value = 0;
10409     change->actual_trigger_ce_score = 0;
10410   }
10411
10412   // do not change elements more than a specified maximum number of changes
10413   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10414     return FALSE;
10415
10416   ChangeCount[x][y]++;          // count number of changes in the same frame
10417
10418   if (change->explode)
10419   {
10420     Bang(x, y);
10421
10422     return TRUE;
10423   }
10424
10425   if (change->use_target_content)
10426   {
10427     boolean complete_replace = TRUE;
10428     boolean can_replace[3][3];
10429     int xx, yy;
10430
10431     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10432     {
10433       boolean is_empty;
10434       boolean is_walkable;
10435       boolean is_diggable;
10436       boolean is_collectible;
10437       boolean is_removable;
10438       boolean is_destructible;
10439       int ex = x + xx - 1;
10440       int ey = y + yy - 1;
10441       int content_element = change->target_content.e[xx][yy];
10442       int e;
10443
10444       can_replace[xx][yy] = TRUE;
10445
10446       if (ex == x && ey == y)   // do not check changing element itself
10447         continue;
10448
10449       if (content_element == EL_EMPTY_SPACE)
10450       {
10451         can_replace[xx][yy] = FALSE;    // do not replace border with space
10452
10453         continue;
10454       }
10455
10456       if (!IN_LEV_FIELD(ex, ey))
10457       {
10458         can_replace[xx][yy] = FALSE;
10459         complete_replace = FALSE;
10460
10461         continue;
10462       }
10463
10464       e = Feld[ex][ey];
10465
10466       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10467         e = MovingOrBlocked2Element(ex, ey);
10468
10469       is_empty = (IS_FREE(ex, ey) ||
10470                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10471
10472       is_walkable     = (is_empty || IS_WALKABLE(e));
10473       is_diggable     = (is_empty || IS_DIGGABLE(e));
10474       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10475       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10476       is_removable    = (is_diggable || is_collectible);
10477
10478       can_replace[xx][yy] =
10479         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10480           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10481           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10482           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10483           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10484           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10485          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10486
10487       if (!can_replace[xx][yy])
10488         complete_replace = FALSE;
10489     }
10490
10491     if (!change->only_if_complete || complete_replace)
10492     {
10493       boolean something_has_changed = FALSE;
10494
10495       if (change->only_if_complete && change->use_random_replace &&
10496           RND(100) < change->random_percentage)
10497         return FALSE;
10498
10499       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10500       {
10501         int ex = x + xx - 1;
10502         int ey = y + yy - 1;
10503         int content_element;
10504
10505         if (can_replace[xx][yy] && (!change->use_random_replace ||
10506                                     RND(100) < change->random_percentage))
10507         {
10508           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10509             RemoveMovingField(ex, ey);
10510
10511           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10512
10513           content_element = change->target_content.e[xx][yy];
10514           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10515                                               ce_value, ce_score);
10516
10517           CreateElementFromChange(ex, ey, target_element);
10518
10519           something_has_changed = TRUE;
10520
10521           // for symmetry reasons, freeze newly created border elements
10522           if (ex != x || ey != y)
10523             Stop[ex][ey] = TRUE;        // no more moving in this frame
10524         }
10525       }
10526
10527       if (something_has_changed)
10528       {
10529         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10530         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10531       }
10532     }
10533   }
10534   else
10535   {
10536     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10537                                         ce_value, ce_score);
10538
10539     if (element == EL_DIAGONAL_GROWING ||
10540         element == EL_DIAGONAL_SHRINKING)
10541     {
10542       target_element = Store[x][y];
10543
10544       Store[x][y] = EL_EMPTY;
10545     }
10546
10547     CreateElementFromChange(x, y, target_element);
10548
10549     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10550     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10551   }
10552
10553   // this uses direct change before indirect change
10554   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10555
10556   return TRUE;
10557 }
10558
10559 static void HandleElementChange(int x, int y, int page)
10560 {
10561   int element = MovingOrBlocked2Element(x, y);
10562   struct ElementInfo *ei = &element_info[element];
10563   struct ElementChangeInfo *change = &ei->change_page[page];
10564   boolean handle_action_before_change = FALSE;
10565
10566 #ifdef DEBUG
10567   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10568       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10569   {
10570     printf("\n\n");
10571     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10572            x, y, element, element_info[element].token_name);
10573     printf("HandleElementChange(): This should never happen!\n");
10574     printf("\n\n");
10575   }
10576 #endif
10577
10578   // this can happen with classic bombs on walkable, changing elements
10579   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10580   {
10581     return;
10582   }
10583
10584   if (ChangeDelay[x][y] == 0)           // initialize element change
10585   {
10586     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10587
10588     if (change->can_change)
10589     {
10590       // !!! not clear why graphic animation should be reset at all here !!!
10591       // !!! UPDATE: but is needed for correct Snake Bite tail animation !!!
10592       // !!! SOLUTION: do not reset if graphics engine set to 4 or above !!!
10593
10594       /*
10595         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10596
10597         When using an animation frame delay of 1 (this only happens with
10598         "sp_zonk.moving.left/right" in the classic graphics), the default
10599         (non-moving) animation shows wrong animation frames (while the
10600         moving animation, like "sp_zonk.moving.left/right", is correct,
10601         so this graphical bug never shows up with the classic graphics).
10602         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10603         be drawn instead of the correct frames 0,1,2,3. This is caused by
10604         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10605         an element change: First when the change delay ("ChangeDelay[][]")
10606         counter has reached zero after decrementing, then a second time in
10607         the next frame (after "GfxFrame[][]" was already incremented) when
10608         "ChangeDelay[][]" is reset to the initial delay value again.
10609
10610         This causes frame 0 to be drawn twice, while the last frame won't
10611         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10612
10613         As some animations may already be cleverly designed around this bug
10614         (at least the "Snake Bite" snake tail animation does this), it cannot
10615         simply be fixed here without breaking such existing animations.
10616         Unfortunately, it cannot easily be detected if a graphics set was
10617         designed "before" or "after" the bug was fixed. As a workaround,
10618         a new graphics set option "game.graphics_engine_version" was added
10619         to be able to specify the game's major release version for which the
10620         graphics set was designed, which can then be used to decide if the
10621         bugfix should be used (version 4 and above) or not (version 3 or
10622         below, or if no version was specified at all, as with old sets).
10623
10624         (The wrong/fixed animation frames can be tested with the test level set
10625         "test_gfxframe" and level "000", which contains a specially prepared
10626         custom element at level position (x/y) == (11/9) which uses the zonk
10627         animation mentioned above. Using "game.graphics_engine_version: 4"
10628         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10629         This can also be seen from the debug output for this test element.)
10630       */
10631
10632       // when a custom element is about to change (for example by change delay),
10633       // do not reset graphic animation when the custom element is moving
10634       if (game.graphics_engine_version < 4 &&
10635           !IS_MOVING(x, y))
10636       {
10637         ResetGfxAnimation(x, y);
10638         ResetRandomAnimationValue(x, y);
10639       }
10640
10641       if (change->pre_change_function)
10642         change->pre_change_function(x, y);
10643     }
10644   }
10645
10646   ChangeDelay[x][y]--;
10647
10648   if (ChangeDelay[x][y] != 0)           // continue element change
10649   {
10650     if (change->can_change)
10651     {
10652       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10653
10654       if (IS_ANIMATED(graphic))
10655         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10656
10657       if (change->change_function)
10658         change->change_function(x, y);
10659     }
10660   }
10661   else                                  // finish element change
10662   {
10663     if (ChangePage[x][y] != -1)         // remember page from delayed change
10664     {
10665       page = ChangePage[x][y];
10666       ChangePage[x][y] = -1;
10667
10668       change = &ei->change_page[page];
10669     }
10670
10671     if (IS_MOVING(x, y))                // never change a running system ;-)
10672     {
10673       ChangeDelay[x][y] = 1;            // try change after next move step
10674       ChangePage[x][y] = page;          // remember page to use for change
10675
10676       return;
10677     }
10678
10679     // special case: set new level random seed before changing element
10680     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10681       handle_action_before_change = TRUE;
10682
10683     if (change->has_action && handle_action_before_change)
10684       ExecuteCustomElementAction(x, y, element, page);
10685
10686     if (change->can_change)
10687     {
10688       if (ChangeElement(x, y, element, page))
10689       {
10690         if (change->post_change_function)
10691           change->post_change_function(x, y);
10692       }
10693     }
10694
10695     if (change->has_action && !handle_action_before_change)
10696       ExecuteCustomElementAction(x, y, element, page);
10697   }
10698 }
10699
10700 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10701                                               int trigger_element,
10702                                               int trigger_event,
10703                                               int trigger_player,
10704                                               int trigger_side,
10705                                               int trigger_page)
10706 {
10707   boolean change_done_any = FALSE;
10708   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10709   int i;
10710
10711   if (!(trigger_events[trigger_element][trigger_event]))
10712     return FALSE;
10713
10714   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10715
10716   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10717   {
10718     int element = EL_CUSTOM_START + i;
10719     boolean change_done = FALSE;
10720     int p;
10721
10722     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10723         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10724       continue;
10725
10726     for (p = 0; p < element_info[element].num_change_pages; p++)
10727     {
10728       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10729
10730       if (change->can_change_or_has_action &&
10731           change->has_event[trigger_event] &&
10732           change->trigger_side & trigger_side &&
10733           change->trigger_player & trigger_player &&
10734           change->trigger_page & trigger_page_bits &&
10735           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10736       {
10737         change->actual_trigger_element = trigger_element;
10738         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10739         change->actual_trigger_player_bits = trigger_player;
10740         change->actual_trigger_side = trigger_side;
10741         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10742         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10743
10744         if ((change->can_change && !change_done) || change->has_action)
10745         {
10746           int x, y;
10747
10748           SCAN_PLAYFIELD(x, y)
10749           {
10750             if (Feld[x][y] == element)
10751             {
10752               if (change->can_change && !change_done)
10753               {
10754                 // if element already changed in this frame, not only prevent
10755                 // another element change (checked in ChangeElement()), but
10756                 // also prevent additional element actions for this element
10757
10758                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10759                     !level.use_action_after_change_bug)
10760                   continue;
10761
10762                 ChangeDelay[x][y] = 1;
10763                 ChangeEvent[x][y] = trigger_event;
10764
10765                 HandleElementChange(x, y, p);
10766               }
10767               else if (change->has_action)
10768               {
10769                 // if element already changed in this frame, not only prevent
10770                 // another element change (checked in ChangeElement()), but
10771                 // also prevent additional element actions for this element
10772
10773                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10774                     !level.use_action_after_change_bug)
10775                   continue;
10776
10777                 ExecuteCustomElementAction(x, y, element, p);
10778                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10779               }
10780             }
10781           }
10782
10783           if (change->can_change)
10784           {
10785             change_done = TRUE;
10786             change_done_any = TRUE;
10787           }
10788         }
10789       }
10790     }
10791   }
10792
10793   RECURSION_LOOP_DETECTION_END();
10794
10795   return change_done_any;
10796 }
10797
10798 static boolean CheckElementChangeExt(int x, int y,
10799                                      int element,
10800                                      int trigger_element,
10801                                      int trigger_event,
10802                                      int trigger_player,
10803                                      int trigger_side)
10804 {
10805   boolean change_done = FALSE;
10806   int p;
10807
10808   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10809       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10810     return FALSE;
10811
10812   if (Feld[x][y] == EL_BLOCKED)
10813   {
10814     Blocked2Moving(x, y, &x, &y);
10815     element = Feld[x][y];
10816   }
10817
10818   // check if element has already changed or is about to change after moving
10819   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10820        Feld[x][y] != element) ||
10821
10822       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10823        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10824         ChangePage[x][y] != -1)))
10825     return FALSE;
10826
10827   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10828
10829   for (p = 0; p < element_info[element].num_change_pages; p++)
10830   {
10831     struct ElementChangeInfo *change = &element_info[element].change_page[p];
10832
10833     /* check trigger element for all events where the element that is checked
10834        for changing interacts with a directly adjacent element -- this is
10835        different to element changes that affect other elements to change on the
10836        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10837     boolean check_trigger_element =
10838       (trigger_event == CE_TOUCHING_X ||
10839        trigger_event == CE_HITTING_X ||
10840        trigger_event == CE_HIT_BY_X ||
10841        trigger_event == CE_DIGGING_X); // this one was forgotten until 3.2.3
10842
10843     if (change->can_change_or_has_action &&
10844         change->has_event[trigger_event] &&
10845         change->trigger_side & trigger_side &&
10846         change->trigger_player & trigger_player &&
10847         (!check_trigger_element ||
10848          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10849     {
10850       change->actual_trigger_element = trigger_element;
10851       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10852       change->actual_trigger_player_bits = trigger_player;
10853       change->actual_trigger_side = trigger_side;
10854       change->actual_trigger_ce_value = CustomValue[x][y];
10855       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10856
10857       // special case: trigger element not at (x,y) position for some events
10858       if (check_trigger_element)
10859       {
10860         static struct
10861         {
10862           int dx, dy;
10863         } move_xy[] =
10864           {
10865             {  0,  0 },
10866             { -1,  0 },
10867             { +1,  0 },
10868             {  0,  0 },
10869             {  0, -1 },
10870             {  0,  0 }, { 0, 0 }, { 0, 0 },
10871             {  0, +1 }
10872           };
10873
10874         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10875         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10876
10877         change->actual_trigger_ce_value = CustomValue[xx][yy];
10878         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10879       }
10880
10881       if (change->can_change && !change_done)
10882       {
10883         ChangeDelay[x][y] = 1;
10884         ChangeEvent[x][y] = trigger_event;
10885
10886         HandleElementChange(x, y, p);
10887
10888         change_done = TRUE;
10889       }
10890       else if (change->has_action)
10891       {
10892         ExecuteCustomElementAction(x, y, element, p);
10893         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10894       }
10895     }
10896   }
10897
10898   RECURSION_LOOP_DETECTION_END();
10899
10900   return change_done;
10901 }
10902
10903 static void PlayPlayerSound(struct PlayerInfo *player)
10904 {
10905   int jx = player->jx, jy = player->jy;
10906   int sound_element = player->artwork_element;
10907   int last_action = player->last_action_waiting;
10908   int action = player->action_waiting;
10909
10910   if (player->is_waiting)
10911   {
10912     if (action != last_action)
10913       PlayLevelSoundElementAction(jx, jy, sound_element, action);
10914     else
10915       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10916   }
10917   else
10918   {
10919     if (action != last_action)
10920       StopSound(element_info[sound_element].sound[last_action]);
10921
10922     if (last_action == ACTION_SLEEPING)
10923       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10924   }
10925 }
10926
10927 static void PlayAllPlayersSound(void)
10928 {
10929   int i;
10930
10931   for (i = 0; i < MAX_PLAYERS; i++)
10932     if (stored_player[i].active)
10933       PlayPlayerSound(&stored_player[i]);
10934 }
10935
10936 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10937 {
10938   boolean last_waiting = player->is_waiting;
10939   int move_dir = player->MovDir;
10940
10941   player->dir_waiting = move_dir;
10942   player->last_action_waiting = player->action_waiting;
10943
10944   if (is_waiting)
10945   {
10946     if (!last_waiting)          // not waiting -> waiting
10947     {
10948       player->is_waiting = TRUE;
10949
10950       player->frame_counter_bored =
10951         FrameCounter +
10952         game.player_boring_delay_fixed +
10953         GetSimpleRandom(game.player_boring_delay_random);
10954       player->frame_counter_sleeping =
10955         FrameCounter +
10956         game.player_sleeping_delay_fixed +
10957         GetSimpleRandom(game.player_sleeping_delay_random);
10958
10959       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10960     }
10961
10962     if (game.player_sleeping_delay_fixed +
10963         game.player_sleeping_delay_random > 0 &&
10964         player->anim_delay_counter == 0 &&
10965         player->post_delay_counter == 0 &&
10966         FrameCounter >= player->frame_counter_sleeping)
10967       player->is_sleeping = TRUE;
10968     else if (game.player_boring_delay_fixed +
10969              game.player_boring_delay_random > 0 &&
10970              FrameCounter >= player->frame_counter_bored)
10971       player->is_bored = TRUE;
10972
10973     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10974                               player->is_bored ? ACTION_BORING :
10975                               ACTION_WAITING);
10976
10977     if (player->is_sleeping && player->use_murphy)
10978     {
10979       // special case for sleeping Murphy when leaning against non-free tile
10980
10981       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10982           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10983            !IS_MOVING(player->jx - 1, player->jy)))
10984         move_dir = MV_LEFT;
10985       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10986                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10987                 !IS_MOVING(player->jx + 1, player->jy)))
10988         move_dir = MV_RIGHT;
10989       else
10990         player->is_sleeping = FALSE;
10991
10992       player->dir_waiting = move_dir;
10993     }
10994
10995     if (player->is_sleeping)
10996     {
10997       if (player->num_special_action_sleeping > 0)
10998       {
10999         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11000         {
11001           int last_special_action = player->special_action_sleeping;
11002           int num_special_action = player->num_special_action_sleeping;
11003           int special_action =
11004             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11005              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11006              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11007              last_special_action + 1 : ACTION_SLEEPING);
11008           int special_graphic =
11009             el_act_dir2img(player->artwork_element, special_action, move_dir);
11010
11011           player->anim_delay_counter =
11012             graphic_info[special_graphic].anim_delay_fixed +
11013             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11014           player->post_delay_counter =
11015             graphic_info[special_graphic].post_delay_fixed +
11016             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11017
11018           player->special_action_sleeping = special_action;
11019         }
11020
11021         if (player->anim_delay_counter > 0)
11022         {
11023           player->action_waiting = player->special_action_sleeping;
11024           player->anim_delay_counter--;
11025         }
11026         else if (player->post_delay_counter > 0)
11027         {
11028           player->post_delay_counter--;
11029         }
11030       }
11031     }
11032     else if (player->is_bored)
11033     {
11034       if (player->num_special_action_bored > 0)
11035       {
11036         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11037         {
11038           int special_action =
11039             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11040           int special_graphic =
11041             el_act_dir2img(player->artwork_element, special_action, move_dir);
11042
11043           player->anim_delay_counter =
11044             graphic_info[special_graphic].anim_delay_fixed +
11045             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11046           player->post_delay_counter =
11047             graphic_info[special_graphic].post_delay_fixed +
11048             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11049
11050           player->special_action_bored = special_action;
11051         }
11052
11053         if (player->anim_delay_counter > 0)
11054         {
11055           player->action_waiting = player->special_action_bored;
11056           player->anim_delay_counter--;
11057         }
11058         else if (player->post_delay_counter > 0)
11059         {
11060           player->post_delay_counter--;
11061         }
11062       }
11063     }
11064   }
11065   else if (last_waiting)        // waiting -> not waiting
11066   {
11067     player->is_waiting = FALSE;
11068     player->is_bored = FALSE;
11069     player->is_sleeping = FALSE;
11070
11071     player->frame_counter_bored = -1;
11072     player->frame_counter_sleeping = -1;
11073
11074     player->anim_delay_counter = 0;
11075     player->post_delay_counter = 0;
11076
11077     player->dir_waiting = player->MovDir;
11078     player->action_waiting = ACTION_DEFAULT;
11079
11080     player->special_action_bored = ACTION_DEFAULT;
11081     player->special_action_sleeping = ACTION_DEFAULT;
11082   }
11083 }
11084
11085 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
11086 {
11087   if ((!player->is_moving  && player->was_moving) ||
11088       (player->MovPos == 0 && player->was_moving) ||
11089       (player->is_snapping && !player->was_snapping) ||
11090       (player->is_dropping && !player->was_dropping))
11091   {
11092     if (!CheckSaveEngineSnapshotToList())
11093       return;
11094
11095     player->was_moving = FALSE;
11096     player->was_snapping = TRUE;
11097     player->was_dropping = TRUE;
11098   }
11099   else
11100   {
11101     if (player->is_moving)
11102       player->was_moving = TRUE;
11103
11104     if (!player->is_snapping)
11105       player->was_snapping = FALSE;
11106
11107     if (!player->is_dropping)
11108       player->was_dropping = FALSE;
11109   }
11110 }
11111
11112 static void CheckSingleStepMode(struct PlayerInfo *player)
11113 {
11114   if (tape.single_step && tape.recording && !tape.pausing)
11115   {
11116     /* as it is called "single step mode", just return to pause mode when the
11117        player stopped moving after one tile (or never starts moving at all) */
11118     if (!player->is_moving &&
11119         !player->is_pushing &&
11120         !player->is_dropping_pressed)
11121       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11122   }
11123
11124   CheckSaveEngineSnapshot(player);
11125 }
11126
11127 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11128 {
11129   int left      = player_action & JOY_LEFT;
11130   int right     = player_action & JOY_RIGHT;
11131   int up        = player_action & JOY_UP;
11132   int down      = player_action & JOY_DOWN;
11133   int button1   = player_action & JOY_BUTTON_1;
11134   int button2   = player_action & JOY_BUTTON_2;
11135   int dx        = (left ? -1 : right ? 1 : 0);
11136   int dy        = (up   ? -1 : down  ? 1 : 0);
11137
11138   if (!player->active || tape.pausing)
11139     return 0;
11140
11141   if (player_action)
11142   {
11143     if (button1)
11144       SnapField(player, dx, dy);
11145     else
11146     {
11147       if (button2)
11148         DropElement(player);
11149
11150       MovePlayer(player, dx, dy);
11151     }
11152
11153     CheckSingleStepMode(player);
11154
11155     SetPlayerWaiting(player, FALSE);
11156
11157     return player_action;
11158   }
11159   else
11160   {
11161     // no actions for this player (no input at player's configured device)
11162
11163     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11164     SnapField(player, 0, 0);
11165     CheckGravityMovementWhenNotMoving(player);
11166
11167     if (player->MovPos == 0)
11168       SetPlayerWaiting(player, TRUE);
11169
11170     if (player->MovPos == 0)    // needed for tape.playing
11171       player->is_moving = FALSE;
11172
11173     player->is_dropping = FALSE;
11174     player->is_dropping_pressed = FALSE;
11175     player->drop_pressed_delay = 0;
11176
11177     CheckSingleStepMode(player);
11178
11179     return 0;
11180   }
11181 }
11182
11183 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11184                                          byte *tape_action)
11185 {
11186   if (!tape.use_mouse_actions)
11187     return;
11188
11189   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11190   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11191   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11192 }
11193
11194 static void SetTapeActionFromMouseAction(byte *tape_action,
11195                                          struct MouseActionInfo *mouse_action)
11196 {
11197   if (!tape.use_mouse_actions)
11198     return;
11199
11200   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11201   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11202   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11203 }
11204
11205 static void CheckLevelSolved(void)
11206 {
11207   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11208   {
11209     if (game_em.level_solved &&
11210         !game_em.game_over)                             // game won
11211     {
11212       LevelSolved();
11213
11214       game_em.game_over = TRUE;
11215
11216       game.all_players_gone = TRUE;
11217     }
11218
11219     if (game_em.game_over)                              // game lost
11220       game.all_players_gone = TRUE;
11221   }
11222   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11223   {
11224     if (game_sp.level_solved &&
11225         !game_sp.game_over)                             // game won
11226     {
11227       LevelSolved();
11228
11229       game_sp.game_over = TRUE;
11230
11231       game.all_players_gone = TRUE;
11232     }
11233
11234     if (game_sp.game_over)                              // game lost
11235       game.all_players_gone = TRUE;
11236   }
11237   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11238   {
11239     if (game_mm.level_solved &&
11240         !game_mm.game_over)                             // game won
11241     {
11242       LevelSolved();
11243
11244       game_mm.game_over = TRUE;
11245
11246       game.all_players_gone = TRUE;
11247     }
11248
11249     if (game_mm.game_over)                              // game lost
11250       game.all_players_gone = TRUE;
11251   }
11252 }
11253
11254 static void CheckLevelTime(void)
11255 {
11256   int i;
11257
11258   if (TimeFrames >= FRAMES_PER_SECOND)
11259   {
11260     TimeFrames = 0;
11261     TapeTime++;
11262
11263     for (i = 0; i < MAX_PLAYERS; i++)
11264     {
11265       struct PlayerInfo *player = &stored_player[i];
11266
11267       if (SHIELD_ON(player))
11268       {
11269         player->shield_normal_time_left--;
11270
11271         if (player->shield_deadly_time_left > 0)
11272           player->shield_deadly_time_left--;
11273       }
11274     }
11275
11276     if (!game.LevelSolved && !level.use_step_counter)
11277     {
11278       TimePlayed++;
11279
11280       if (TimeLeft > 0)
11281       {
11282         TimeLeft--;
11283
11284         if (TimeLeft <= 10 && setup.time_limit)
11285           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11286
11287         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11288            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11289
11290         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11291
11292         if (!TimeLeft && setup.time_limit)
11293         {
11294           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11295             game_em.lev->killed_out_of_time = TRUE;
11296           else
11297             for (i = 0; i < MAX_PLAYERS; i++)
11298               KillPlayer(&stored_player[i]);
11299         }
11300       }
11301       else if (game.no_time_limit && !game.all_players_gone)
11302       {
11303         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11304       }
11305
11306       game_em.lev->time = (game.no_time_limit ? TimePlayed : TimeLeft);
11307     }
11308
11309     if (tape.recording || tape.playing)
11310       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11311   }
11312
11313   if (tape.recording || tape.playing)
11314     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11315
11316   UpdateAndDisplayGameControlValues();
11317 }
11318
11319 void AdvanceFrameAndPlayerCounters(int player_nr)
11320 {
11321   int i;
11322
11323   // advance frame counters (global frame counter and time frame counter)
11324   FrameCounter++;
11325   TimeFrames++;
11326
11327   // advance player counters (counters for move delay, move animation etc.)
11328   for (i = 0; i < MAX_PLAYERS; i++)
11329   {
11330     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11331     int move_delay_value = stored_player[i].move_delay_value;
11332     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11333
11334     if (!advance_player_counters)       // not all players may be affected
11335       continue;
11336
11337     if (move_frames == 0)       // less than one move per game frame
11338     {
11339       int stepsize = TILEX / move_delay_value;
11340       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11341       int count = (stored_player[i].is_moving ?
11342                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11343
11344       if (count % delay == 0)
11345         move_frames = 1;
11346     }
11347
11348     stored_player[i].Frame += move_frames;
11349
11350     if (stored_player[i].MovPos != 0)
11351       stored_player[i].StepFrame += move_frames;
11352
11353     if (stored_player[i].move_delay > 0)
11354       stored_player[i].move_delay--;
11355
11356     // due to bugs in previous versions, counter must count up, not down
11357     if (stored_player[i].push_delay != -1)
11358       stored_player[i].push_delay++;
11359
11360     if (stored_player[i].drop_delay > 0)
11361       stored_player[i].drop_delay--;
11362
11363     if (stored_player[i].is_dropping_pressed)
11364       stored_player[i].drop_pressed_delay++;
11365   }
11366 }
11367
11368 void StartGameActions(boolean init_network_game, boolean record_tape,
11369                       int random_seed)
11370 {
11371   unsigned int new_random_seed = InitRND(random_seed);
11372
11373   if (record_tape)
11374     TapeStartRecording(new_random_seed);
11375
11376   if (init_network_game)
11377   {
11378     SendToServer_LevelFile();
11379     SendToServer_StartPlaying();
11380
11381     return;
11382   }
11383
11384   InitGame();
11385 }
11386
11387 static void GameActionsExt(void)
11388 {
11389 #if 0
11390   static unsigned int game_frame_delay = 0;
11391 #endif
11392   unsigned int game_frame_delay_value;
11393   byte *recorded_player_action;
11394   byte summarized_player_action = 0;
11395   byte tape_action[MAX_TAPE_ACTIONS] = { 0 };
11396   int i;
11397
11398   // detect endless loops, caused by custom element programming
11399   if (recursion_loop_detected && recursion_loop_depth == 0)
11400   {
11401     char *message = getStringCat3("Internal Error! Element ",
11402                                   EL_NAME(recursion_loop_element),
11403                                   " caused endless loop! Quit the game?");
11404
11405     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11406           EL_NAME(recursion_loop_element));
11407
11408     RequestQuitGameExt(FALSE, level_editor_test_game, message);
11409
11410     recursion_loop_detected = FALSE;    // if game should be continued
11411
11412     free(message);
11413
11414     return;
11415   }
11416
11417   if (game.restart_level)
11418     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11419
11420   CheckLevelSolved();
11421
11422   if (game.LevelSolved && !game.LevelSolved_GameEnd)
11423     GameWon();
11424
11425   if (game.all_players_gone && !TAPE_IS_STOPPED(tape))
11426     TapeStop();
11427
11428   if (game_status != GAME_MODE_PLAYING)         // status might have changed
11429     return;
11430
11431   game_frame_delay_value =
11432     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11433
11434   if (tape.playing && tape.warp_forward && !tape.pausing)
11435     game_frame_delay_value = 0;
11436
11437   SetVideoFrameDelay(game_frame_delay_value);
11438
11439   // (de)activate virtual buttons depending on current game status
11440   if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
11441   {
11442     if (game.all_players_gone)  // if no players there to be controlled anymore
11443       SetOverlayActive(FALSE);
11444     else if (!tape.playing)     // if game continues after tape stopped playing
11445       SetOverlayActive(TRUE);
11446   }
11447
11448 #if 0
11449 #if 0
11450   // ---------- main game synchronization point ----------
11451
11452   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11453
11454   printf("::: skip == %d\n", skip);
11455
11456 #else
11457   // ---------- main game synchronization point ----------
11458
11459   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11460 #endif
11461 #endif
11462
11463   if (network_playing && !network_player_action_received)
11464   {
11465     // try to get network player actions in time
11466
11467     // last chance to get network player actions without main loop delay
11468     HandleNetworking();
11469
11470     // game was quit by network peer
11471     if (game_status != GAME_MODE_PLAYING)
11472       return;
11473
11474     // check if network player actions still missing and game still running
11475     if (!network_player_action_received && !checkGameEnded())
11476       return;           // failed to get network player actions in time
11477
11478     // do not yet reset "network_player_action_received" (for tape.pausing)
11479   }
11480
11481   if (tape.pausing)
11482     return;
11483
11484   // at this point we know that we really continue executing the game
11485
11486   network_player_action_received = FALSE;
11487
11488   // when playing tape, read previously recorded player input from tape data
11489   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11490
11491   local_player->effective_mouse_action = local_player->mouse_action;
11492
11493   if (recorded_player_action != NULL)
11494     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11495                                  recorded_player_action);
11496
11497   // TapePlayAction() may return NULL when toggling to "pause before death"
11498   if (tape.pausing)
11499     return;
11500
11501   if (tape.set_centered_player)
11502   {
11503     game.centered_player_nr_next = tape.centered_player_nr_next;
11504     game.set_centered_player = TRUE;
11505   }
11506
11507   for (i = 0; i < MAX_PLAYERS; i++)
11508   {
11509     summarized_player_action |= stored_player[i].action;
11510
11511     if (!network_playing && (game.team_mode || tape.playing))
11512       stored_player[i].effective_action = stored_player[i].action;
11513   }
11514
11515   if (network_playing && !checkGameEnded())
11516     SendToServer_MovePlayer(summarized_player_action);
11517
11518   // summarize all actions at local players mapped input device position
11519   // (this allows using different input devices in single player mode)
11520   if (!network.enabled && !game.team_mode)
11521     stored_player[map_player_action[local_player->index_nr]].effective_action =
11522       summarized_player_action;
11523
11524   // summarize all actions at centered player in local team mode
11525   if (tape.recording &&
11526       setup.team_mode && !network.enabled &&
11527       setup.input_on_focus &&
11528       game.centered_player_nr != -1)
11529   {
11530     for (i = 0; i < MAX_PLAYERS; i++)
11531       stored_player[map_player_action[i]].effective_action =
11532         (i == game.centered_player_nr ? summarized_player_action : 0);
11533   }
11534
11535   if (recorded_player_action != NULL)
11536     for (i = 0; i < MAX_PLAYERS; i++)
11537       stored_player[i].effective_action = recorded_player_action[i];
11538
11539   for (i = 0; i < MAX_PLAYERS; i++)
11540   {
11541     tape_action[i] = stored_player[i].effective_action;
11542
11543     /* (this may happen in the RND game engine if a player was not present on
11544        the playfield on level start, but appeared later from a custom element */
11545     if (setup.team_mode &&
11546         tape.recording &&
11547         tape_action[i] &&
11548         !tape.player_participates[i])
11549       tape.player_participates[i] = TRUE;
11550   }
11551
11552   SetTapeActionFromMouseAction(tape_action,
11553                                &local_player->effective_mouse_action);
11554
11555   // only record actions from input devices, but not programmed actions
11556   if (tape.recording)
11557     TapeRecordAction(tape_action);
11558
11559   // remember if game was played (especially after tape stopped playing)
11560   if (!tape.playing && summarized_player_action)
11561     game.GamePlayed = TRUE;
11562
11563 #if USE_NEW_PLAYER_ASSIGNMENTS
11564   // !!! also map player actions in single player mode !!!
11565   // if (game.team_mode)
11566   if (1)
11567   {
11568     byte mapped_action[MAX_PLAYERS];
11569
11570 #if DEBUG_PLAYER_ACTIONS
11571     printf(":::");
11572     for (i = 0; i < MAX_PLAYERS; i++)
11573       printf(" %d, ", stored_player[i].effective_action);
11574 #endif
11575
11576     for (i = 0; i < MAX_PLAYERS; i++)
11577       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11578
11579     for (i = 0; i < MAX_PLAYERS; i++)
11580       stored_player[i].effective_action = mapped_action[i];
11581
11582 #if DEBUG_PLAYER_ACTIONS
11583     printf(" =>");
11584     for (i = 0; i < MAX_PLAYERS; i++)
11585       printf(" %d, ", stored_player[i].effective_action);
11586     printf("\n");
11587 #endif
11588   }
11589 #if DEBUG_PLAYER_ACTIONS
11590   else
11591   {
11592     printf(":::");
11593     for (i = 0; i < MAX_PLAYERS; i++)
11594       printf(" %d, ", stored_player[i].effective_action);
11595     printf("\n");
11596   }
11597 #endif
11598 #endif
11599
11600   for (i = 0; i < MAX_PLAYERS; i++)
11601   {
11602     // allow engine snapshot in case of changed movement attempt
11603     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11604         (stored_player[i].effective_action & KEY_MOTION))
11605       game.snapshot.changed_action = TRUE;
11606
11607     // allow engine snapshot in case of snapping/dropping attempt
11608     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11609         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11610       game.snapshot.changed_action = TRUE;
11611
11612     game.snapshot.last_action[i] = stored_player[i].effective_action;
11613   }
11614
11615   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11616   {
11617     GameActions_EM_Main();
11618   }
11619   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11620   {
11621     GameActions_SP_Main();
11622   }
11623   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11624   {
11625     GameActions_MM_Main();
11626   }
11627   else
11628   {
11629     GameActions_RND_Main();
11630   }
11631
11632   BlitScreenToBitmap(backbuffer);
11633
11634   CheckLevelSolved();
11635   CheckLevelTime();
11636
11637   AdvanceFrameAndPlayerCounters(-1);    // advance counters for all players
11638
11639   if (global.show_frames_per_second)
11640   {
11641     static unsigned int fps_counter = 0;
11642     static int fps_frames = 0;
11643     unsigned int fps_delay_ms = Counter() - fps_counter;
11644
11645     fps_frames++;
11646
11647     if (fps_delay_ms >= 500)    // calculate FPS every 0.5 seconds
11648     {
11649       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11650
11651       fps_frames = 0;
11652       fps_counter = Counter();
11653
11654       // always draw FPS to screen after FPS value was updated
11655       redraw_mask |= REDRAW_FPS;
11656     }
11657
11658     // only draw FPS if no screen areas are deactivated (invisible warp mode)
11659     if (GetDrawDeactivationMask() == REDRAW_NONE)
11660       redraw_mask |= REDRAW_FPS;
11661   }
11662 }
11663
11664 static void GameActions_CheckSaveEngineSnapshot(void)
11665 {
11666   if (!game.snapshot.save_snapshot)
11667     return;
11668
11669   // clear flag for saving snapshot _before_ saving snapshot
11670   game.snapshot.save_snapshot = FALSE;
11671
11672   SaveEngineSnapshotToList();
11673 }
11674
11675 void GameActions(void)
11676 {
11677   GameActionsExt();
11678
11679   GameActions_CheckSaveEngineSnapshot();
11680 }
11681
11682 void GameActions_EM_Main(void)
11683 {
11684   byte effective_action[MAX_PLAYERS];
11685   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11686   int i;
11687
11688   for (i = 0; i < MAX_PLAYERS; i++)
11689     effective_action[i] = stored_player[i].effective_action;
11690
11691   GameActions_EM(effective_action, warp_mode);
11692 }
11693
11694 void GameActions_SP_Main(void)
11695 {
11696   byte effective_action[MAX_PLAYERS];
11697   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11698   int i;
11699
11700   for (i = 0; i < MAX_PLAYERS; i++)
11701     effective_action[i] = stored_player[i].effective_action;
11702
11703   GameActions_SP(effective_action, warp_mode);
11704
11705   for (i = 0; i < MAX_PLAYERS; i++)
11706   {
11707     if (stored_player[i].force_dropping)
11708       stored_player[i].action |= KEY_BUTTON_DROP;
11709
11710     stored_player[i].force_dropping = FALSE;
11711   }
11712 }
11713
11714 void GameActions_MM_Main(void)
11715 {
11716   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11717
11718   GameActions_MM(local_player->effective_mouse_action, warp_mode);
11719 }
11720
11721 void GameActions_RND_Main(void)
11722 {
11723   GameActions_RND();
11724 }
11725
11726 void GameActions_RND(void)
11727 {
11728   static struct MouseActionInfo mouse_action_last = { 0 };
11729   struct MouseActionInfo mouse_action = local_player->effective_mouse_action;
11730   int magic_wall_x = 0, magic_wall_y = 0;
11731   int i, x, y, element, graphic, last_gfx_frame;
11732
11733   InitPlayfieldScanModeVars();
11734
11735   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11736   {
11737     SCAN_PLAYFIELD(x, y)
11738     {
11739       ChangeCount[x][y] = 0;
11740       ChangeEvent[x][y] = -1;
11741     }
11742   }
11743
11744   if (game.set_centered_player)
11745   {
11746     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11747
11748     // switching to "all players" only possible if all players fit to screen
11749     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11750     {
11751       game.centered_player_nr_next = game.centered_player_nr;
11752       game.set_centered_player = FALSE;
11753     }
11754
11755     // do not switch focus to non-existing (or non-active) player
11756     if (game.centered_player_nr_next >= 0 &&
11757         !stored_player[game.centered_player_nr_next].active)
11758     {
11759       game.centered_player_nr_next = game.centered_player_nr;
11760       game.set_centered_player = FALSE;
11761     }
11762   }
11763
11764   if (game.set_centered_player &&
11765       ScreenMovPos == 0)        // screen currently aligned at tile position
11766   {
11767     int sx, sy;
11768
11769     if (game.centered_player_nr_next == -1)
11770     {
11771       setScreenCenteredToAllPlayers(&sx, &sy);
11772     }
11773     else
11774     {
11775       sx = stored_player[game.centered_player_nr_next].jx;
11776       sy = stored_player[game.centered_player_nr_next].jy;
11777     }
11778
11779     game.centered_player_nr = game.centered_player_nr_next;
11780     game.set_centered_player = FALSE;
11781
11782     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11783     DrawGameDoorValues();
11784   }
11785
11786   for (i = 0; i < MAX_PLAYERS; i++)
11787   {
11788     int actual_player_action = stored_player[i].effective_action;
11789
11790 #if 1
11791     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11792        - rnd_equinox_tetrachloride 048
11793        - rnd_equinox_tetrachloride_ii 096
11794        - rnd_emanuel_schmieg 002
11795        - doctor_sloan_ww 001, 020
11796     */
11797     if (stored_player[i].MovPos == 0)
11798       CheckGravityMovement(&stored_player[i]);
11799 #endif
11800
11801     // overwrite programmed action with tape action
11802     if (stored_player[i].programmed_action)
11803       actual_player_action = stored_player[i].programmed_action;
11804
11805     PlayerActions(&stored_player[i], actual_player_action);
11806
11807     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11808   }
11809
11810   ScrollScreen(NULL, SCROLL_GO_ON);
11811
11812   /* for backwards compatibility, the following code emulates a fixed bug that
11813      occured when pushing elements (causing elements that just made their last
11814      pushing step to already (if possible) make their first falling step in the
11815      same game frame, which is bad); this code is also needed to use the famous
11816      "spring push bug" which is used in older levels and might be wanted to be
11817      used also in newer levels, but in this case the buggy pushing code is only
11818      affecting the "spring" element and no other elements */
11819
11820   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11821   {
11822     for (i = 0; i < MAX_PLAYERS; i++)
11823     {
11824       struct PlayerInfo *player = &stored_player[i];
11825       int x = player->jx;
11826       int y = player->jy;
11827
11828       if (player->active && player->is_pushing && player->is_moving &&
11829           IS_MOVING(x, y) &&
11830           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11831            Feld[x][y] == EL_SPRING))
11832       {
11833         ContinueMoving(x, y);
11834
11835         // continue moving after pushing (this is actually a bug)
11836         if (!IS_MOVING(x, y))
11837           Stop[x][y] = FALSE;
11838       }
11839     }
11840   }
11841
11842   SCAN_PLAYFIELD(x, y)
11843   {
11844     Last[x][y] = Feld[x][y];
11845
11846     ChangeCount[x][y] = 0;
11847     ChangeEvent[x][y] = -1;
11848
11849     // this must be handled before main playfield loop
11850     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11851     {
11852       MovDelay[x][y]--;
11853       if (MovDelay[x][y] <= 0)
11854         RemoveField(x, y);
11855     }
11856
11857     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11858     {
11859       MovDelay[x][y]--;
11860       if (MovDelay[x][y] <= 0)
11861       {
11862         RemoveField(x, y);
11863         TEST_DrawLevelField(x, y);
11864
11865         TestIfElementTouchesCustomElement(x, y);        // for empty space
11866       }
11867     }
11868
11869 #if DEBUG
11870     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11871     {
11872       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11873       printf("GameActions(): This should never happen!\n");
11874
11875       ChangePage[x][y] = -1;
11876     }
11877 #endif
11878
11879     Stop[x][y] = FALSE;
11880     if (WasJustMoving[x][y] > 0)
11881       WasJustMoving[x][y]--;
11882     if (WasJustFalling[x][y] > 0)
11883       WasJustFalling[x][y]--;
11884     if (CheckCollision[x][y] > 0)
11885       CheckCollision[x][y]--;
11886     if (CheckImpact[x][y] > 0)
11887       CheckImpact[x][y]--;
11888
11889     GfxFrame[x][y]++;
11890
11891     /* reset finished pushing action (not done in ContinueMoving() to allow
11892        continuous pushing animation for elements with zero push delay) */
11893     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11894     {
11895       ResetGfxAnimation(x, y);
11896       TEST_DrawLevelField(x, y);
11897     }
11898
11899 #if DEBUG
11900     if (IS_BLOCKED(x, y))
11901     {
11902       int oldx, oldy;
11903
11904       Blocked2Moving(x, y, &oldx, &oldy);
11905       if (!IS_MOVING(oldx, oldy))
11906       {
11907         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11908         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11909         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11910         printf("GameActions(): This should never happen!\n");
11911       }
11912     }
11913 #endif
11914   }
11915
11916   if (mouse_action.button)
11917   {
11918     int new_button = (mouse_action.button && mouse_action_last.button == 0);
11919
11920     x = mouse_action.lx;
11921     y = mouse_action.ly;
11922     element = Feld[x][y];
11923
11924     if (new_button)
11925     {
11926       CheckElementChange(x, y, element, EL_UNDEFINED, CE_CLICKED_BY_MOUSE);
11927       CheckTriggeredElementChange(x, y, element, CE_MOUSE_CLICKED_ON_X);
11928     }
11929
11930     CheckElementChange(x, y, element, EL_UNDEFINED, CE_PRESSED_BY_MOUSE);
11931     CheckTriggeredElementChange(x, y, element, CE_MOUSE_PRESSED_ON_X);
11932   }
11933
11934   SCAN_PLAYFIELD(x, y)
11935   {
11936     element = Feld[x][y];
11937     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11938     last_gfx_frame = GfxFrame[x][y];
11939
11940     ResetGfxFrame(x, y);
11941
11942     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
11943       DrawLevelGraphicAnimation(x, y, graphic);
11944
11945     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11946         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11947       ResetRandomAnimationValue(x, y);
11948
11949     SetRandomAnimationValue(x, y);
11950
11951     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11952
11953     if (IS_INACTIVE(element))
11954     {
11955       if (IS_ANIMATED(graphic))
11956         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11957
11958       continue;
11959     }
11960
11961     // this may take place after moving, so 'element' may have changed
11962     if (IS_CHANGING(x, y) &&
11963         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11964     {
11965       int page = element_info[element].event_page_nr[CE_DELAY];
11966
11967       HandleElementChange(x, y, page);
11968
11969       element = Feld[x][y];
11970       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11971     }
11972
11973     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11974     {
11975       StartMoving(x, y);
11976
11977       element = Feld[x][y];
11978       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11979
11980       if (IS_ANIMATED(graphic) &&
11981           !IS_MOVING(x, y) &&
11982           !Stop[x][y])
11983         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11984
11985       if (IS_GEM(element) || element == EL_SP_INFOTRON)
11986         TEST_DrawTwinkleOnField(x, y);
11987     }
11988     else if (element == EL_ACID)
11989     {
11990       if (!Stop[x][y])
11991         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11992     }
11993     else if ((element == EL_EXIT_OPEN ||
11994               element == EL_EM_EXIT_OPEN ||
11995               element == EL_SP_EXIT_OPEN ||
11996               element == EL_STEEL_EXIT_OPEN ||
11997               element == EL_EM_STEEL_EXIT_OPEN ||
11998               element == EL_SP_TERMINAL ||
11999               element == EL_SP_TERMINAL_ACTIVE ||
12000               element == EL_EXTRA_TIME ||
12001               element == EL_SHIELD_NORMAL ||
12002               element == EL_SHIELD_DEADLY) &&
12003              IS_ANIMATED(graphic))
12004       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12005     else if (IS_MOVING(x, y))
12006       ContinueMoving(x, y);
12007     else if (IS_ACTIVE_BOMB(element))
12008       CheckDynamite(x, y);
12009     else if (element == EL_AMOEBA_GROWING)
12010       AmoebeWaechst(x, y);
12011     else if (element == EL_AMOEBA_SHRINKING)
12012       AmoebaDisappearing(x, y);
12013
12014 #if !USE_NEW_AMOEBA_CODE
12015     else if (IS_AMOEBALIVE(element))
12016       AmoebeAbleger(x, y);
12017 #endif
12018
12019     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12020       Life(x, y);
12021     else if (element == EL_EXIT_CLOSED)
12022       CheckExit(x, y);
12023     else if (element == EL_EM_EXIT_CLOSED)
12024       CheckExitEM(x, y);
12025     else if (element == EL_STEEL_EXIT_CLOSED)
12026       CheckExitSteel(x, y);
12027     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12028       CheckExitSteelEM(x, y);
12029     else if (element == EL_SP_EXIT_CLOSED)
12030       CheckExitSP(x, y);
12031     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12032              element == EL_EXPANDABLE_STEELWALL_GROWING)
12033       MauerWaechst(x, y);
12034     else if (element == EL_EXPANDABLE_WALL ||
12035              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12036              element == EL_EXPANDABLE_WALL_VERTICAL ||
12037              element == EL_EXPANDABLE_WALL_ANY ||
12038              element == EL_BD_EXPANDABLE_WALL)
12039       MauerAbleger(x, y);
12040     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12041              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12042              element == EL_EXPANDABLE_STEELWALL_ANY)
12043       MauerAblegerStahl(x, y);
12044     else if (element == EL_FLAMES)
12045       CheckForDragon(x, y);
12046     else if (element == EL_EXPLOSION)
12047       ; // drawing of correct explosion animation is handled separately
12048     else if (element == EL_ELEMENT_SNAPPING ||
12049              element == EL_DIAGONAL_SHRINKING ||
12050              element == EL_DIAGONAL_GROWING)
12051     {
12052       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12053
12054       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12055     }
12056     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12057       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12058
12059     if (IS_BELT_ACTIVE(element))
12060       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12061
12062     if (game.magic_wall_active)
12063     {
12064       int jx = local_player->jx, jy = local_player->jy;
12065
12066       // play the element sound at the position nearest to the player
12067       if ((element == EL_MAGIC_WALL_FULL ||
12068            element == EL_MAGIC_WALL_ACTIVE ||
12069            element == EL_MAGIC_WALL_EMPTYING ||
12070            element == EL_BD_MAGIC_WALL_FULL ||
12071            element == EL_BD_MAGIC_WALL_ACTIVE ||
12072            element == EL_BD_MAGIC_WALL_EMPTYING ||
12073            element == EL_DC_MAGIC_WALL_FULL ||
12074            element == EL_DC_MAGIC_WALL_ACTIVE ||
12075            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12076           ABS(x - jx) + ABS(y - jy) <
12077           ABS(magic_wall_x - jx) + ABS(magic_wall_y - jy))
12078       {
12079         magic_wall_x = x;
12080         magic_wall_y = y;
12081       }
12082     }
12083   }
12084
12085 #if USE_NEW_AMOEBA_CODE
12086   // new experimental amoeba growth stuff
12087   if (!(FrameCounter % 8))
12088   {
12089     static unsigned int random = 1684108901;
12090
12091     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12092     {
12093       x = RND(lev_fieldx);
12094       y = RND(lev_fieldy);
12095       element = Feld[x][y];
12096
12097       if (!IS_PLAYER(x,y) &&
12098           (element == EL_EMPTY ||
12099            CAN_GROW_INTO(element) ||
12100            element == EL_QUICKSAND_EMPTY ||
12101            element == EL_QUICKSAND_FAST_EMPTY ||
12102            element == EL_ACID_SPLASH_LEFT ||
12103            element == EL_ACID_SPLASH_RIGHT))
12104       {
12105         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
12106             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
12107             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
12108             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
12109           Feld[x][y] = EL_AMOEBA_DROP;
12110       }
12111
12112       random = random * 129 + 1;
12113     }
12114   }
12115 #endif
12116
12117   game.explosions_delayed = FALSE;
12118
12119   SCAN_PLAYFIELD(x, y)
12120   {
12121     element = Feld[x][y];
12122
12123     if (ExplodeField[x][y])
12124       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12125     else if (element == EL_EXPLOSION)
12126       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12127
12128     ExplodeField[x][y] = EX_TYPE_NONE;
12129   }
12130
12131   game.explosions_delayed = TRUE;
12132
12133   if (game.magic_wall_active)
12134   {
12135     if (!(game.magic_wall_time_left % 4))
12136     {
12137       int element = Feld[magic_wall_x][magic_wall_y];
12138
12139       if (element == EL_BD_MAGIC_WALL_FULL ||
12140           element == EL_BD_MAGIC_WALL_ACTIVE ||
12141           element == EL_BD_MAGIC_WALL_EMPTYING)
12142         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12143       else if (element == EL_DC_MAGIC_WALL_FULL ||
12144                element == EL_DC_MAGIC_WALL_ACTIVE ||
12145                element == EL_DC_MAGIC_WALL_EMPTYING)
12146         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12147       else
12148         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12149     }
12150
12151     if (game.magic_wall_time_left > 0)
12152     {
12153       game.magic_wall_time_left--;
12154
12155       if (!game.magic_wall_time_left)
12156       {
12157         SCAN_PLAYFIELD(x, y)
12158         {
12159           element = Feld[x][y];
12160
12161           if (element == EL_MAGIC_WALL_ACTIVE ||
12162               element == EL_MAGIC_WALL_FULL)
12163           {
12164             Feld[x][y] = EL_MAGIC_WALL_DEAD;
12165             TEST_DrawLevelField(x, y);
12166           }
12167           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12168                    element == EL_BD_MAGIC_WALL_FULL)
12169           {
12170             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
12171             TEST_DrawLevelField(x, y);
12172           }
12173           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12174                    element == EL_DC_MAGIC_WALL_FULL)
12175           {
12176             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
12177             TEST_DrawLevelField(x, y);
12178           }
12179         }
12180
12181         game.magic_wall_active = FALSE;
12182       }
12183     }
12184   }
12185
12186   if (game.light_time_left > 0)
12187   {
12188     game.light_time_left--;
12189
12190     if (game.light_time_left == 0)
12191       RedrawAllLightSwitchesAndInvisibleElements();
12192   }
12193
12194   if (game.timegate_time_left > 0)
12195   {
12196     game.timegate_time_left--;
12197
12198     if (game.timegate_time_left == 0)
12199       CloseAllOpenTimegates();
12200   }
12201
12202   if (game.lenses_time_left > 0)
12203   {
12204     game.lenses_time_left--;
12205
12206     if (game.lenses_time_left == 0)
12207       RedrawAllInvisibleElementsForLenses();
12208   }
12209
12210   if (game.magnify_time_left > 0)
12211   {
12212     game.magnify_time_left--;
12213
12214     if (game.magnify_time_left == 0)
12215       RedrawAllInvisibleElementsForMagnifier();
12216   }
12217
12218   for (i = 0; i < MAX_PLAYERS; i++)
12219   {
12220     struct PlayerInfo *player = &stored_player[i];
12221
12222     if (SHIELD_ON(player))
12223     {
12224       if (player->shield_deadly_time_left)
12225         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12226       else if (player->shield_normal_time_left)
12227         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12228     }
12229   }
12230
12231 #if USE_DELAYED_GFX_REDRAW
12232   SCAN_PLAYFIELD(x, y)
12233   {
12234     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12235     {
12236       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12237          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12238
12239       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12240         DrawLevelField(x, y);
12241
12242       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12243         DrawLevelFieldCrumbled(x, y);
12244
12245       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12246         DrawLevelFieldCrumbledNeighbours(x, y);
12247
12248       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12249         DrawTwinkleOnField(x, y);
12250     }
12251
12252     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12253   }
12254 #endif
12255
12256   DrawAllPlayers();
12257   PlayAllPlayersSound();
12258
12259   for (i = 0; i < MAX_PLAYERS; i++)
12260   {
12261     struct PlayerInfo *player = &stored_player[i];
12262
12263     if (player->show_envelope != 0 && (!player->active ||
12264                                        player->MovPos == 0))
12265     {
12266       ShowEnvelope(player->show_envelope - EL_ENVELOPE_1);
12267
12268       player->show_envelope = 0;
12269     }
12270   }
12271
12272   // use random number generator in every frame to make it less predictable
12273   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12274     RND(1);
12275
12276   mouse_action_last = mouse_action;
12277 }
12278
12279 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12280 {
12281   int min_x = x, min_y = y, max_x = x, max_y = y;
12282   int i;
12283
12284   for (i = 0; i < MAX_PLAYERS; i++)
12285   {
12286     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12287
12288     if (!stored_player[i].active || &stored_player[i] == player)
12289       continue;
12290
12291     min_x = MIN(min_x, jx);
12292     min_y = MIN(min_y, jy);
12293     max_x = MAX(max_x, jx);
12294     max_y = MAX(max_y, jy);
12295   }
12296
12297   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12298 }
12299
12300 static boolean AllPlayersInVisibleScreen(void)
12301 {
12302   int i;
12303
12304   for (i = 0; i < MAX_PLAYERS; i++)
12305   {
12306     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12307
12308     if (!stored_player[i].active)
12309       continue;
12310
12311     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12312       return FALSE;
12313   }
12314
12315   return TRUE;
12316 }
12317
12318 void ScrollLevel(int dx, int dy)
12319 {
12320   int scroll_offset = 2 * TILEX_VAR;
12321   int x, y;
12322
12323   BlitBitmap(drawto_field, drawto_field,
12324              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12325              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12326              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12327              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12328              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12329              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12330
12331   if (dx != 0)
12332   {
12333     x = (dx == 1 ? BX1 : BX2);
12334     for (y = BY1; y <= BY2; y++)
12335       DrawScreenField(x, y);
12336   }
12337
12338   if (dy != 0)
12339   {
12340     y = (dy == 1 ? BY1 : BY2);
12341     for (x = BX1; x <= BX2; x++)
12342       DrawScreenField(x, y);
12343   }
12344
12345   redraw_mask |= REDRAW_FIELD;
12346 }
12347
12348 static boolean canFallDown(struct PlayerInfo *player)
12349 {
12350   int jx = player->jx, jy = player->jy;
12351
12352   return (IN_LEV_FIELD(jx, jy + 1) &&
12353           (IS_FREE(jx, jy + 1) ||
12354            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12355           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
12356           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
12357 }
12358
12359 static boolean canPassField(int x, int y, int move_dir)
12360 {
12361   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12362   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12363   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12364   int nextx = x + dx;
12365   int nexty = y + dy;
12366   int element = Feld[x][y];
12367
12368   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12369           !CAN_MOVE(element) &&
12370           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12371           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
12372           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12373 }
12374
12375 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12376 {
12377   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12378   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12379   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12380   int newx = x + dx;
12381   int newy = y + dy;
12382
12383   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12384           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12385           (IS_DIGGABLE(Feld[newx][newy]) ||
12386            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12387            canPassField(newx, newy, move_dir)));
12388 }
12389
12390 static void CheckGravityMovement(struct PlayerInfo *player)
12391 {
12392   if (player->gravity && !player->programmed_action)
12393   {
12394     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12395     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12396     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12397     int jx = player->jx, jy = player->jy;
12398     boolean player_is_moving_to_valid_field =
12399       (!player_is_snapping &&
12400        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12401         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12402     boolean player_can_fall_down = canFallDown(player);
12403
12404     if (player_can_fall_down &&
12405         !player_is_moving_to_valid_field)
12406       player->programmed_action = MV_DOWN;
12407   }
12408 }
12409
12410 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12411 {
12412   return CheckGravityMovement(player);
12413
12414   if (player->gravity && !player->programmed_action)
12415   {
12416     int jx = player->jx, jy = player->jy;
12417     boolean field_under_player_is_free =
12418       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12419     boolean player_is_standing_on_valid_field =
12420       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12421        (IS_WALKABLE(Feld[jx][jy]) &&
12422         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12423
12424     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12425       player->programmed_action = MV_DOWN;
12426   }
12427 }
12428
12429 /*
12430   MovePlayerOneStep()
12431   -----------------------------------------------------------------------------
12432   dx, dy:               direction (non-diagonal) to try to move the player to
12433   real_dx, real_dy:     direction as read from input device (can be diagonal)
12434 */
12435
12436 boolean MovePlayerOneStep(struct PlayerInfo *player,
12437                           int dx, int dy, int real_dx, int real_dy)
12438 {
12439   int jx = player->jx, jy = player->jy;
12440   int new_jx = jx + dx, new_jy = jy + dy;
12441   int can_move;
12442   boolean player_can_move = !player->cannot_move;
12443
12444   if (!player->active || (!dx && !dy))
12445     return MP_NO_ACTION;
12446
12447   player->MovDir = (dx < 0 ? MV_LEFT :
12448                     dx > 0 ? MV_RIGHT :
12449                     dy < 0 ? MV_UP :
12450                     dy > 0 ? MV_DOWN :  MV_NONE);
12451
12452   if (!IN_LEV_FIELD(new_jx, new_jy))
12453     return MP_NO_ACTION;
12454
12455   if (!player_can_move)
12456   {
12457     if (player->MovPos == 0)
12458     {
12459       player->is_moving = FALSE;
12460       player->is_digging = FALSE;
12461       player->is_collecting = FALSE;
12462       player->is_snapping = FALSE;
12463       player->is_pushing = FALSE;
12464     }
12465   }
12466
12467   if (!network.enabled && game.centered_player_nr == -1 &&
12468       !AllPlayersInSight(player, new_jx, new_jy))
12469     return MP_NO_ACTION;
12470
12471   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12472   if (can_move != MP_MOVING)
12473     return can_move;
12474
12475   // check if DigField() has caused relocation of the player
12476   if (player->jx != jx || player->jy != jy)
12477     return MP_NO_ACTION;        // <-- !!! CHECK THIS [-> MP_ACTION ?] !!!
12478
12479   StorePlayer[jx][jy] = 0;
12480   player->last_jx = jx;
12481   player->last_jy = jy;
12482   player->jx = new_jx;
12483   player->jy = new_jy;
12484   StorePlayer[new_jx][new_jy] = player->element_nr;
12485
12486   if (player->move_delay_value_next != -1)
12487   {
12488     player->move_delay_value = player->move_delay_value_next;
12489     player->move_delay_value_next = -1;
12490   }
12491
12492   player->MovPos =
12493     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12494
12495   player->step_counter++;
12496
12497   PlayerVisit[jx][jy] = FrameCounter;
12498
12499   player->is_moving = TRUE;
12500
12501 #if 1
12502   // should better be called in MovePlayer(), but this breaks some tapes
12503   ScrollPlayer(player, SCROLL_INIT);
12504 #endif
12505
12506   return MP_MOVING;
12507 }
12508
12509 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12510 {
12511   int jx = player->jx, jy = player->jy;
12512   int old_jx = jx, old_jy = jy;
12513   int moved = MP_NO_ACTION;
12514
12515   if (!player->active)
12516     return FALSE;
12517
12518   if (!dx && !dy)
12519   {
12520     if (player->MovPos == 0)
12521     {
12522       player->is_moving = FALSE;
12523       player->is_digging = FALSE;
12524       player->is_collecting = FALSE;
12525       player->is_snapping = FALSE;
12526       player->is_pushing = FALSE;
12527     }
12528
12529     return FALSE;
12530   }
12531
12532   if (player->move_delay > 0)
12533     return FALSE;
12534
12535   player->move_delay = -1;              // set to "uninitialized" value
12536
12537   // store if player is automatically moved to next field
12538   player->is_auto_moving = (player->programmed_action != MV_NONE);
12539
12540   // remove the last programmed player action
12541   player->programmed_action = 0;
12542
12543   if (player->MovPos)
12544   {
12545     // should only happen if pre-1.2 tape recordings are played
12546     // this is only for backward compatibility
12547
12548     int original_move_delay_value = player->move_delay_value;
12549
12550 #if DEBUG
12551     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
12552            tape.counter);
12553 #endif
12554
12555     // scroll remaining steps with finest movement resolution
12556     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12557
12558     while (player->MovPos)
12559     {
12560       ScrollPlayer(player, SCROLL_GO_ON);
12561       ScrollScreen(NULL, SCROLL_GO_ON);
12562
12563       AdvanceFrameAndPlayerCounters(player->index_nr);
12564
12565       DrawAllPlayers();
12566       BackToFront_WithFrameDelay(0);
12567     }
12568
12569     player->move_delay_value = original_move_delay_value;
12570   }
12571
12572   player->is_active = FALSE;
12573
12574   if (player->last_move_dir & MV_HORIZONTAL)
12575   {
12576     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12577       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12578   }
12579   else
12580   {
12581     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12582       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12583   }
12584
12585   if (!moved && !player->is_active)
12586   {
12587     player->is_moving = FALSE;
12588     player->is_digging = FALSE;
12589     player->is_collecting = FALSE;
12590     player->is_snapping = FALSE;
12591     player->is_pushing = FALSE;
12592   }
12593
12594   jx = player->jx;
12595   jy = player->jy;
12596
12597   if (moved & MP_MOVING && !ScreenMovPos &&
12598       (player->index_nr == game.centered_player_nr ||
12599        game.centered_player_nr == -1))
12600   {
12601     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12602
12603     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12604     {
12605       // actual player has left the screen -- scroll in that direction
12606       if (jx != old_jx)         // player has moved horizontally
12607         scroll_x += (jx - old_jx);
12608       else                      // player has moved vertically
12609         scroll_y += (jy - old_jy);
12610     }
12611     else
12612     {
12613       int offset_raw = game.scroll_delay_value;
12614
12615       if (jx != old_jx)         // player has moved horizontally
12616       {
12617         int offset = MIN(offset_raw, (SCR_FIELDX - 2) / 2);
12618         int offset_x = offset * (player->MovDir == MV_LEFT ? +1 : -1);
12619         int new_scroll_x = jx - MIDPOSX + offset_x;
12620
12621         if ((player->MovDir == MV_LEFT  && scroll_x > new_scroll_x) ||
12622             (player->MovDir == MV_RIGHT && scroll_x < new_scroll_x))
12623           scroll_x = new_scroll_x;
12624
12625         // don't scroll over playfield boundaries
12626         scroll_x = MIN(MAX(SBX_Left, scroll_x), SBX_Right);
12627
12628         // don't scroll more than one field at a time
12629         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12630
12631         // don't scroll against the player's moving direction
12632         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12633             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12634           scroll_x = old_scroll_x;
12635       }
12636       else                      // player has moved vertically
12637       {
12638         int offset = MIN(offset_raw, (SCR_FIELDY - 2) / 2);
12639         int offset_y = offset * (player->MovDir == MV_UP ? +1 : -1);
12640         int new_scroll_y = jy - MIDPOSY + offset_y;
12641
12642         if ((player->MovDir == MV_UP   && scroll_y > new_scroll_y) ||
12643             (player->MovDir == MV_DOWN && scroll_y < new_scroll_y))
12644           scroll_y = new_scroll_y;
12645
12646         // don't scroll over playfield boundaries
12647         scroll_y = MIN(MAX(SBY_Upper, scroll_y), SBY_Lower);
12648
12649         // don't scroll more than one field at a time
12650         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12651
12652         // don't scroll against the player's moving direction
12653         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12654             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12655           scroll_y = old_scroll_y;
12656       }
12657     }
12658
12659     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12660     {
12661       if (!network.enabled && game.centered_player_nr == -1 &&
12662           !AllPlayersInVisibleScreen())
12663       {
12664         scroll_x = old_scroll_x;
12665         scroll_y = old_scroll_y;
12666       }
12667       else
12668       {
12669         ScrollScreen(player, SCROLL_INIT);
12670         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12671       }
12672     }
12673   }
12674
12675   player->StepFrame = 0;
12676
12677   if (moved & MP_MOVING)
12678   {
12679     if (old_jx != jx && old_jy == jy)
12680       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12681     else if (old_jx == jx && old_jy != jy)
12682       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12683
12684     TEST_DrawLevelField(jx, jy);        // for "crumbled sand"
12685
12686     player->last_move_dir = player->MovDir;
12687     player->is_moving = TRUE;
12688     player->is_snapping = FALSE;
12689     player->is_switching = FALSE;
12690     player->is_dropping = FALSE;
12691     player->is_dropping_pressed = FALSE;
12692     player->drop_pressed_delay = 0;
12693
12694 #if 0
12695     // should better be called here than above, but this breaks some tapes
12696     ScrollPlayer(player, SCROLL_INIT);
12697 #endif
12698   }
12699   else
12700   {
12701     CheckGravityMovementWhenNotMoving(player);
12702
12703     player->is_moving = FALSE;
12704
12705     /* at this point, the player is allowed to move, but cannot move right now
12706        (e.g. because of something blocking the way) -- ensure that the player
12707        is also allowed to move in the next frame (in old versions before 3.1.1,
12708        the player was forced to wait again for eight frames before next try) */
12709
12710     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12711       player->move_delay = 0;   // allow direct movement in the next frame
12712   }
12713
12714   if (player->move_delay == -1)         // not yet initialized by DigField()
12715     player->move_delay = player->move_delay_value;
12716
12717   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12718   {
12719     TestIfPlayerTouchesBadThing(jx, jy);
12720     TestIfPlayerTouchesCustomElement(jx, jy);
12721   }
12722
12723   if (!player->active)
12724     RemovePlayer(player);
12725
12726   return moved;
12727 }
12728
12729 void ScrollPlayer(struct PlayerInfo *player, int mode)
12730 {
12731   int jx = player->jx, jy = player->jy;
12732   int last_jx = player->last_jx, last_jy = player->last_jy;
12733   int move_stepsize = TILEX / player->move_delay_value;
12734
12735   if (!player->active)
12736     return;
12737
12738   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      // player not moving
12739     return;
12740
12741   if (mode == SCROLL_INIT)
12742   {
12743     player->actual_frame_counter = FrameCounter;
12744     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12745
12746     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12747         Feld[last_jx][last_jy] == EL_EMPTY)
12748     {
12749       int last_field_block_delay = 0;   // start with no blocking at all
12750       int block_delay_adjustment = player->block_delay_adjustment;
12751
12752       // if player blocks last field, add delay for exactly one move
12753       if (player->block_last_field)
12754       {
12755         last_field_block_delay += player->move_delay_value;
12756
12757         // when blocking enabled, prevent moving up despite gravity
12758         if (player->gravity && player->MovDir == MV_UP)
12759           block_delay_adjustment = -1;
12760       }
12761
12762       // add block delay adjustment (also possible when not blocking)
12763       last_field_block_delay += block_delay_adjustment;
12764
12765       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12766       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12767     }
12768
12769     if (player->MovPos != 0)    // player has not yet reached destination
12770       return;
12771   }
12772   else if (!FrameReached(&player->actual_frame_counter, 1))
12773     return;
12774
12775   if (player->MovPos != 0)
12776   {
12777     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12778     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12779
12780     // before DrawPlayer() to draw correct player graphic for this case
12781     if (player->MovPos == 0)
12782       CheckGravityMovement(player);
12783   }
12784
12785   if (player->MovPos == 0)      // player reached destination field
12786   {
12787     if (player->move_delay_reset_counter > 0)
12788     {
12789       player->move_delay_reset_counter--;
12790
12791       if (player->move_delay_reset_counter == 0)
12792       {
12793         // continue with normal speed after quickly moving through gate
12794         HALVE_PLAYER_SPEED(player);
12795
12796         // be able to make the next move without delay
12797         player->move_delay = 0;
12798       }
12799     }
12800
12801     player->last_jx = jx;
12802     player->last_jy = jy;
12803
12804     if (Feld[jx][jy] == EL_EXIT_OPEN ||
12805         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12806         Feld[jx][jy] == EL_EM_EXIT_OPENING ||
12807         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12808         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12809         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12810         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12811         Feld[jx][jy] == EL_SP_EXIT_OPENING)     // <-- special case
12812     {
12813       ExitPlayer(player);
12814
12815       if (game.players_still_needed == 0 &&
12816           (game.friends_still_needed == 0 ||
12817            IS_SP_ELEMENT(Feld[jx][jy])))
12818         LevelSolved();
12819     }
12820
12821     // this breaks one level: "machine", level 000
12822     {
12823       int move_direction = player->MovDir;
12824       int enter_side = MV_DIR_OPPOSITE(move_direction);
12825       int leave_side = move_direction;
12826       int old_jx = last_jx;
12827       int old_jy = last_jy;
12828       int old_element = Feld[old_jx][old_jy];
12829       int new_element = Feld[jx][jy];
12830
12831       if (IS_CUSTOM_ELEMENT(old_element))
12832         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12833                                    CE_LEFT_BY_PLAYER,
12834                                    player->index_bit, leave_side);
12835
12836       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12837                                           CE_PLAYER_LEAVES_X,
12838                                           player->index_bit, leave_side);
12839
12840       if (IS_CUSTOM_ELEMENT(new_element))
12841         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12842                                    player->index_bit, enter_side);
12843
12844       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12845                                           CE_PLAYER_ENTERS_X,
12846                                           player->index_bit, enter_side);
12847
12848       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12849                                         CE_MOVE_OF_X, move_direction);
12850     }
12851
12852     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12853     {
12854       TestIfPlayerTouchesBadThing(jx, jy);
12855       TestIfPlayerTouchesCustomElement(jx, jy);
12856
12857       /* needed because pushed element has not yet reached its destination,
12858          so it would trigger a change event at its previous field location */
12859       if (!player->is_pushing)
12860         TestIfElementTouchesCustomElement(jx, jy);      // for empty space
12861
12862       if (!player->active)
12863         RemovePlayer(player);
12864     }
12865
12866     if (!game.LevelSolved && level.use_step_counter)
12867     {
12868       int i;
12869
12870       TimePlayed++;
12871
12872       if (TimeLeft > 0)
12873       {
12874         TimeLeft--;
12875
12876         if (TimeLeft <= 10 && setup.time_limit)
12877           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12878
12879         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12880
12881         DisplayGameControlValues();
12882
12883         if (!TimeLeft && setup.time_limit)
12884           for (i = 0; i < MAX_PLAYERS; i++)
12885             KillPlayer(&stored_player[i]);
12886       }
12887       else if (game.no_time_limit && !game.all_players_gone)
12888       {
12889         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12890
12891         DisplayGameControlValues();
12892       }
12893     }
12894
12895     if (tape.single_step && tape.recording && !tape.pausing &&
12896         !player->programmed_action)
12897       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12898
12899     if (!player->programmed_action)
12900       CheckSaveEngineSnapshot(player);
12901   }
12902 }
12903
12904 void ScrollScreen(struct PlayerInfo *player, int mode)
12905 {
12906   static unsigned int screen_frame_counter = 0;
12907
12908   if (mode == SCROLL_INIT)
12909   {
12910     // set scrolling step size according to actual player's moving speed
12911     ScrollStepSize = TILEX / player->move_delay_value;
12912
12913     screen_frame_counter = FrameCounter;
12914     ScreenMovDir = player->MovDir;
12915     ScreenMovPos = player->MovPos;
12916     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12917     return;
12918   }
12919   else if (!FrameReached(&screen_frame_counter, 1))
12920     return;
12921
12922   if (ScreenMovPos)
12923   {
12924     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12925     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12926     redraw_mask |= REDRAW_FIELD;
12927   }
12928   else
12929     ScreenMovDir = MV_NONE;
12930 }
12931
12932 void TestIfPlayerTouchesCustomElement(int x, int y)
12933 {
12934   static int xy[4][2] =
12935   {
12936     { 0, -1 },
12937     { -1, 0 },
12938     { +1, 0 },
12939     { 0, +1 }
12940   };
12941   static int trigger_sides[4][2] =
12942   {
12943     // center side       border side
12944     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
12945     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
12946     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
12947     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
12948   };
12949   static int touch_dir[4] =
12950   {
12951     MV_LEFT | MV_RIGHT,
12952     MV_UP   | MV_DOWN,
12953     MV_UP   | MV_DOWN,
12954     MV_LEFT | MV_RIGHT
12955   };
12956   int center_element = Feld[x][y];      // should always be non-moving!
12957   int i;
12958
12959   for (i = 0; i < NUM_DIRECTIONS; i++)
12960   {
12961     int xx = x + xy[i][0];
12962     int yy = y + xy[i][1];
12963     int center_side = trigger_sides[i][0];
12964     int border_side = trigger_sides[i][1];
12965     int border_element;
12966
12967     if (!IN_LEV_FIELD(xx, yy))
12968       continue;
12969
12970     if (IS_PLAYER(x, y))                // player found at center element
12971     {
12972       struct PlayerInfo *player = PLAYERINFO(x, y);
12973
12974       if (game.engine_version < VERSION_IDENT(3,0,7,0))
12975         border_element = Feld[xx][yy];          // may be moving!
12976       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12977         border_element = Feld[xx][yy];
12978       else if (MovDir[xx][yy] & touch_dir[i])   // elements are touching
12979         border_element = MovingOrBlocked2Element(xx, yy);
12980       else
12981         continue;               // center and border element do not touch
12982
12983       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12984                                  player->index_bit, border_side);
12985       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12986                                           CE_PLAYER_TOUCHES_X,
12987                                           player->index_bit, border_side);
12988
12989       {
12990         /* use player element that is initially defined in the level playfield,
12991            not the player element that corresponds to the runtime player number
12992            (example: a level that contains EL_PLAYER_3 as the only player would
12993            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12994         int player_element = PLAYERINFO(x, y)->initial_element;
12995
12996         CheckElementChangeBySide(xx, yy, border_element, player_element,
12997                                  CE_TOUCHING_X, border_side);
12998       }
12999     }
13000     else if (IS_PLAYER(xx, yy))         // player found at border element
13001     {
13002       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13003
13004       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13005       {
13006         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13007           continue;             // center and border element do not touch
13008       }
13009
13010       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13011                                  player->index_bit, center_side);
13012       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13013                                           CE_PLAYER_TOUCHES_X,
13014                                           player->index_bit, center_side);
13015
13016       {
13017         /* use player element that is initially defined in the level playfield,
13018            not the player element that corresponds to the runtime player number
13019            (example: a level that contains EL_PLAYER_3 as the only player would
13020            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13021         int player_element = PLAYERINFO(xx, yy)->initial_element;
13022
13023         CheckElementChangeBySide(x, y, center_element, player_element,
13024                                  CE_TOUCHING_X, center_side);
13025       }
13026
13027       break;
13028     }
13029   }
13030 }
13031
13032 void TestIfElementTouchesCustomElement(int x, int y)
13033 {
13034   static int xy[4][2] =
13035   {
13036     { 0, -1 },
13037     { -1, 0 },
13038     { +1, 0 },
13039     { 0, +1 }
13040   };
13041   static int trigger_sides[4][2] =
13042   {
13043     // center side      border side
13044     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13045     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13046     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13047     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13048   };
13049   static int touch_dir[4] =
13050   {
13051     MV_LEFT | MV_RIGHT,
13052     MV_UP   | MV_DOWN,
13053     MV_UP   | MV_DOWN,
13054     MV_LEFT | MV_RIGHT
13055   };
13056   boolean change_center_element = FALSE;
13057   int center_element = Feld[x][y];      // should always be non-moving!
13058   int border_element_old[NUM_DIRECTIONS];
13059   int i;
13060
13061   for (i = 0; i < NUM_DIRECTIONS; i++)
13062   {
13063     int xx = x + xy[i][0];
13064     int yy = y + xy[i][1];
13065     int border_element;
13066
13067     border_element_old[i] = -1;
13068
13069     if (!IN_LEV_FIELD(xx, yy))
13070       continue;
13071
13072     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13073       border_element = Feld[xx][yy];    // may be moving!
13074     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13075       border_element = Feld[xx][yy];
13076     else if (MovDir[xx][yy] & touch_dir[i])     // elements are touching
13077       border_element = MovingOrBlocked2Element(xx, yy);
13078     else
13079       continue;                 // center and border element do not touch
13080
13081     border_element_old[i] = border_element;
13082   }
13083
13084   for (i = 0; i < NUM_DIRECTIONS; i++)
13085   {
13086     int xx = x + xy[i][0];
13087     int yy = y + xy[i][1];
13088     int center_side = trigger_sides[i][0];
13089     int border_element = border_element_old[i];
13090
13091     if (border_element == -1)
13092       continue;
13093
13094     // check for change of border element
13095     CheckElementChangeBySide(xx, yy, border_element, center_element,
13096                              CE_TOUCHING_X, center_side);
13097
13098     // (center element cannot be player, so we dont have to check this here)
13099   }
13100
13101   for (i = 0; i < NUM_DIRECTIONS; i++)
13102   {
13103     int xx = x + xy[i][0];
13104     int yy = y + xy[i][1];
13105     int border_side = trigger_sides[i][1];
13106     int border_element = border_element_old[i];
13107
13108     if (border_element == -1)
13109       continue;
13110
13111     // check for change of center element (but change it only once)
13112     if (!change_center_element)
13113       change_center_element =
13114         CheckElementChangeBySide(x, y, center_element, border_element,
13115                                  CE_TOUCHING_X, border_side);
13116
13117     if (IS_PLAYER(xx, yy))
13118     {
13119       /* use player element that is initially defined in the level playfield,
13120          not the player element that corresponds to the runtime player number
13121          (example: a level that contains EL_PLAYER_3 as the only player would
13122          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13123       int player_element = PLAYERINFO(xx, yy)->initial_element;
13124
13125       CheckElementChangeBySide(x, y, center_element, player_element,
13126                                CE_TOUCHING_X, border_side);
13127     }
13128   }
13129 }
13130
13131 void TestIfElementHitsCustomElement(int x, int y, int direction)
13132 {
13133   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13134   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13135   int hitx = x + dx, hity = y + dy;
13136   int hitting_element = Feld[x][y];
13137   int touched_element;
13138
13139   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13140     return;
13141
13142   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13143                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13144
13145   if (IN_LEV_FIELD(hitx, hity))
13146   {
13147     int opposite_direction = MV_DIR_OPPOSITE(direction);
13148     int hitting_side = direction;
13149     int touched_side = opposite_direction;
13150     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13151                           MovDir[hitx][hity] != direction ||
13152                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13153
13154     object_hit = TRUE;
13155
13156     if (object_hit)
13157     {
13158       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13159                                CE_HITTING_X, touched_side);
13160
13161       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13162                                CE_HIT_BY_X, hitting_side);
13163
13164       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13165                                CE_HIT_BY_SOMETHING, opposite_direction);
13166
13167       if (IS_PLAYER(hitx, hity))
13168       {
13169         /* use player element that is initially defined in the level playfield,
13170            not the player element that corresponds to the runtime player number
13171            (example: a level that contains EL_PLAYER_3 as the only player would
13172            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13173         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13174
13175         CheckElementChangeBySide(x, y, hitting_element, player_element,
13176                                  CE_HITTING_X, touched_side);
13177       }
13178     }
13179   }
13180
13181   // "hitting something" is also true when hitting the playfield border
13182   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13183                            CE_HITTING_SOMETHING, direction);
13184 }
13185
13186 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13187 {
13188   int i, kill_x = -1, kill_y = -1;
13189
13190   int bad_element = -1;
13191   static int test_xy[4][2] =
13192   {
13193     { 0, -1 },
13194     { -1, 0 },
13195     { +1, 0 },
13196     { 0, +1 }
13197   };
13198   static int test_dir[4] =
13199   {
13200     MV_UP,
13201     MV_LEFT,
13202     MV_RIGHT,
13203     MV_DOWN
13204   };
13205
13206   for (i = 0; i < NUM_DIRECTIONS; i++)
13207   {
13208     int test_x, test_y, test_move_dir, test_element;
13209
13210     test_x = good_x + test_xy[i][0];
13211     test_y = good_y + test_xy[i][1];
13212
13213     if (!IN_LEV_FIELD(test_x, test_y))
13214       continue;
13215
13216     test_move_dir =
13217       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13218
13219     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13220
13221     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13222        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13223     */
13224     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13225         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13226     {
13227       kill_x = test_x;
13228       kill_y = test_y;
13229       bad_element = test_element;
13230
13231       break;
13232     }
13233   }
13234
13235   if (kill_x != -1 || kill_y != -1)
13236   {
13237     if (IS_PLAYER(good_x, good_y))
13238     {
13239       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13240
13241       if (player->shield_deadly_time_left > 0 &&
13242           !IS_INDESTRUCTIBLE(bad_element))
13243         Bang(kill_x, kill_y);
13244       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13245         KillPlayer(player);
13246     }
13247     else
13248       Bang(good_x, good_y);
13249   }
13250 }
13251
13252 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13253 {
13254   int i, kill_x = -1, kill_y = -1;
13255   int bad_element = Feld[bad_x][bad_y];
13256   static int test_xy[4][2] =
13257   {
13258     { 0, -1 },
13259     { -1, 0 },
13260     { +1, 0 },
13261     { 0, +1 }
13262   };
13263   static int touch_dir[4] =
13264   {
13265     MV_LEFT | MV_RIGHT,
13266     MV_UP   | MV_DOWN,
13267     MV_UP   | MV_DOWN,
13268     MV_LEFT | MV_RIGHT
13269   };
13270   static int test_dir[4] =
13271   {
13272     MV_UP,
13273     MV_LEFT,
13274     MV_RIGHT,
13275     MV_DOWN
13276   };
13277
13278   if (bad_element == EL_EXPLOSION)      // skip just exploding bad things
13279     return;
13280
13281   for (i = 0; i < NUM_DIRECTIONS; i++)
13282   {
13283     int test_x, test_y, test_move_dir, test_element;
13284
13285     test_x = bad_x + test_xy[i][0];
13286     test_y = bad_y + test_xy[i][1];
13287
13288     if (!IN_LEV_FIELD(test_x, test_y))
13289       continue;
13290
13291     test_move_dir =
13292       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13293
13294     test_element = Feld[test_x][test_y];
13295
13296     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13297        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13298     */
13299     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13300         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13301     {
13302       // good thing is player or penguin that does not move away
13303       if (IS_PLAYER(test_x, test_y))
13304       {
13305         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13306
13307         if (bad_element == EL_ROBOT && player->is_moving)
13308           continue;     // robot does not kill player if he is moving
13309
13310         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13311         {
13312           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13313             continue;           // center and border element do not touch
13314         }
13315
13316         kill_x = test_x;
13317         kill_y = test_y;
13318
13319         break;
13320       }
13321       else if (test_element == EL_PENGUIN)
13322       {
13323         kill_x = test_x;
13324         kill_y = test_y;
13325
13326         break;
13327       }
13328     }
13329   }
13330
13331   if (kill_x != -1 || kill_y != -1)
13332   {
13333     if (IS_PLAYER(kill_x, kill_y))
13334     {
13335       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13336
13337       if (player->shield_deadly_time_left > 0 &&
13338           !IS_INDESTRUCTIBLE(bad_element))
13339         Bang(bad_x, bad_y);
13340       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13341         KillPlayer(player);
13342     }
13343     else
13344       Bang(kill_x, kill_y);
13345   }
13346 }
13347
13348 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13349 {
13350   int bad_element = Feld[bad_x][bad_y];
13351   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13352   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13353   int test_x = bad_x + dx, test_y = bad_y + dy;
13354   int test_move_dir, test_element;
13355   int kill_x = -1, kill_y = -1;
13356
13357   if (!IN_LEV_FIELD(test_x, test_y))
13358     return;
13359
13360   test_move_dir =
13361     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13362
13363   test_element = Feld[test_x][test_y];
13364
13365   if (test_move_dir != bad_move_dir)
13366   {
13367     // good thing can be player or penguin that does not move away
13368     if (IS_PLAYER(test_x, test_y))
13369     {
13370       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13371
13372       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13373          player as being hit when he is moving towards the bad thing, because
13374          the "get hit by" condition would be lost after the player stops) */
13375       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13376         return;         // player moves away from bad thing
13377
13378       kill_x = test_x;
13379       kill_y = test_y;
13380     }
13381     else if (test_element == EL_PENGUIN)
13382     {
13383       kill_x = test_x;
13384       kill_y = test_y;
13385     }
13386   }
13387
13388   if (kill_x != -1 || kill_y != -1)
13389   {
13390     if (IS_PLAYER(kill_x, kill_y))
13391     {
13392       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13393
13394       if (player->shield_deadly_time_left > 0 &&
13395           !IS_INDESTRUCTIBLE(bad_element))
13396         Bang(bad_x, bad_y);
13397       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13398         KillPlayer(player);
13399     }
13400     else
13401       Bang(kill_x, kill_y);
13402   }
13403 }
13404
13405 void TestIfPlayerTouchesBadThing(int x, int y)
13406 {
13407   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13408 }
13409
13410 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13411 {
13412   TestIfGoodThingHitsBadThing(x, y, move_dir);
13413 }
13414
13415 void TestIfBadThingTouchesPlayer(int x, int y)
13416 {
13417   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13418 }
13419
13420 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13421 {
13422   TestIfBadThingHitsGoodThing(x, y, move_dir);
13423 }
13424
13425 void TestIfFriendTouchesBadThing(int x, int y)
13426 {
13427   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13428 }
13429
13430 void TestIfBadThingTouchesFriend(int x, int y)
13431 {
13432   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13433 }
13434
13435 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13436 {
13437   int i, kill_x = bad_x, kill_y = bad_y;
13438   static int xy[4][2] =
13439   {
13440     { 0, -1 },
13441     { -1, 0 },
13442     { +1, 0 },
13443     { 0, +1 }
13444   };
13445
13446   for (i = 0; i < NUM_DIRECTIONS; i++)
13447   {
13448     int x, y, element;
13449
13450     x = bad_x + xy[i][0];
13451     y = bad_y + xy[i][1];
13452     if (!IN_LEV_FIELD(x, y))
13453       continue;
13454
13455     element = Feld[x][y];
13456     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13457         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13458     {
13459       kill_x = x;
13460       kill_y = y;
13461       break;
13462     }
13463   }
13464
13465   if (kill_x != bad_x || kill_y != bad_y)
13466     Bang(bad_x, bad_y);
13467 }
13468
13469 void KillPlayer(struct PlayerInfo *player)
13470 {
13471   int jx = player->jx, jy = player->jy;
13472
13473   if (!player->active)
13474     return;
13475
13476 #if 0
13477   printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
13478          player->killed, player->active, player->reanimated);
13479 #endif
13480
13481   /* the following code was introduced to prevent an infinite loop when calling
13482      -> Bang()
13483      -> CheckTriggeredElementChangeExt()
13484      -> ExecuteCustomElementAction()
13485      -> KillPlayer()
13486      -> (infinitely repeating the above sequence of function calls)
13487      which occurs when killing the player while having a CE with the setting
13488      "kill player X when explosion of <player X>"; the solution using a new
13489      field "player->killed" was chosen for backwards compatibility, although
13490      clever use of the fields "player->active" etc. would probably also work */
13491 #if 1
13492   if (player->killed)
13493     return;
13494 #endif
13495
13496   player->killed = TRUE;
13497
13498   // remove accessible field at the player's position
13499   Feld[jx][jy] = EL_EMPTY;
13500
13501   // deactivate shield (else Bang()/Explode() would not work right)
13502   player->shield_normal_time_left = 0;
13503   player->shield_deadly_time_left = 0;
13504
13505 #if 0
13506   printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
13507          player->killed, player->active, player->reanimated);
13508 #endif
13509
13510   Bang(jx, jy);
13511
13512 #if 0
13513   printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
13514          player->killed, player->active, player->reanimated);
13515 #endif
13516
13517   if (player->reanimated)       // killed player may have been reanimated
13518     player->killed = player->reanimated = FALSE;
13519   else
13520     BuryPlayer(player);
13521 }
13522
13523 static void KillPlayerUnlessEnemyProtected(int x, int y)
13524 {
13525   if (!PLAYER_ENEMY_PROTECTED(x, y))
13526     KillPlayer(PLAYERINFO(x, y));
13527 }
13528
13529 static void KillPlayerUnlessExplosionProtected(int x, int y)
13530 {
13531   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13532     KillPlayer(PLAYERINFO(x, y));
13533 }
13534
13535 void BuryPlayer(struct PlayerInfo *player)
13536 {
13537   int jx = player->jx, jy = player->jy;
13538
13539   if (!player->active)
13540     return;
13541
13542   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13543   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13544
13545   RemovePlayer(player);
13546
13547   player->buried = TRUE;
13548
13549   if (game.all_players_gone)
13550     game.GameOver = TRUE;
13551 }
13552
13553 void RemovePlayer(struct PlayerInfo *player)
13554 {
13555   int jx = player->jx, jy = player->jy;
13556   int i, found = FALSE;
13557
13558   player->present = FALSE;
13559   player->active = FALSE;
13560
13561   // required for some CE actions (even if the player is not active anymore)
13562   player->MovPos = 0;
13563
13564   if (!ExplodeField[jx][jy])
13565     StorePlayer[jx][jy] = 0;
13566
13567   if (player->is_moving)
13568     TEST_DrawLevelField(player->last_jx, player->last_jy);
13569
13570   for (i = 0; i < MAX_PLAYERS; i++)
13571     if (stored_player[i].active)
13572       found = TRUE;
13573
13574   if (!found)
13575   {
13576     game.all_players_gone = TRUE;
13577     game.GameOver = TRUE;
13578   }
13579
13580   game.exit_x = game.robot_wheel_x = jx;
13581   game.exit_y = game.robot_wheel_y = jy;
13582 }
13583
13584 void ExitPlayer(struct PlayerInfo *player)
13585 {
13586   DrawPlayer(player);   // needed here only to cleanup last field
13587   RemovePlayer(player);
13588
13589   if (game.players_still_needed > 0)
13590     game.players_still_needed--;
13591 }
13592
13593 static void setFieldForSnapping(int x, int y, int element, int direction)
13594 {
13595   struct ElementInfo *ei = &element_info[element];
13596   int direction_bit = MV_DIR_TO_BIT(direction);
13597   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13598   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13599                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13600
13601   Feld[x][y] = EL_ELEMENT_SNAPPING;
13602   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13603
13604   ResetGfxAnimation(x, y);
13605
13606   GfxElement[x][y] = element;
13607   GfxAction[x][y] = action;
13608   GfxDir[x][y] = direction;
13609   GfxFrame[x][y] = -1;
13610 }
13611
13612 /*
13613   =============================================================================
13614   checkDiagonalPushing()
13615   -----------------------------------------------------------------------------
13616   check if diagonal input device direction results in pushing of object
13617   (by checking if the alternative direction is walkable, diggable, ...)
13618   =============================================================================
13619 */
13620
13621 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13622                                     int x, int y, int real_dx, int real_dy)
13623 {
13624   int jx, jy, dx, dy, xx, yy;
13625
13626   if (real_dx == 0 || real_dy == 0)     // no diagonal direction => push
13627     return TRUE;
13628
13629   // diagonal direction: check alternative direction
13630   jx = player->jx;
13631   jy = player->jy;
13632   dx = x - jx;
13633   dy = y - jy;
13634   xx = jx + (dx == 0 ? real_dx : 0);
13635   yy = jy + (dy == 0 ? real_dy : 0);
13636
13637   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13638 }
13639
13640 /*
13641   =============================================================================
13642   DigField()
13643   -----------------------------------------------------------------------------
13644   x, y:                 field next to player (non-diagonal) to try to dig to
13645   real_dx, real_dy:     direction as read from input device (can be diagonal)
13646   =============================================================================
13647 */
13648
13649 static int DigField(struct PlayerInfo *player,
13650                     int oldx, int oldy, int x, int y,
13651                     int real_dx, int real_dy, int mode)
13652 {
13653   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13654   boolean player_was_pushing = player->is_pushing;
13655   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13656   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13657   int jx = oldx, jy = oldy;
13658   int dx = x - jx, dy = y - jy;
13659   int nextx = x + dx, nexty = y + dy;
13660   int move_direction = (dx == -1 ? MV_LEFT  :
13661                         dx == +1 ? MV_RIGHT :
13662                         dy == -1 ? MV_UP    :
13663                         dy == +1 ? MV_DOWN  : MV_NONE);
13664   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13665   int dig_side = MV_DIR_OPPOSITE(move_direction);
13666   int old_element = Feld[jx][jy];
13667   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13668   int collect_count;
13669
13670   if (is_player)                // function can also be called by EL_PENGUIN
13671   {
13672     if (player->MovPos == 0)
13673     {
13674       player->is_digging = FALSE;
13675       player->is_collecting = FALSE;
13676     }
13677
13678     if (player->MovPos == 0)    // last pushing move finished
13679       player->is_pushing = FALSE;
13680
13681     if (mode == DF_NO_PUSH)     // player just stopped pushing
13682     {
13683       player->is_switching = FALSE;
13684       player->push_delay = -1;
13685
13686       return MP_NO_ACTION;
13687     }
13688   }
13689
13690   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13691     old_element = Back[jx][jy];
13692
13693   // in case of element dropped at player position, check background
13694   else if (Back[jx][jy] != EL_EMPTY &&
13695            game.engine_version >= VERSION_IDENT(2,2,0,0))
13696     old_element = Back[jx][jy];
13697
13698   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13699     return MP_NO_ACTION;        // field has no opening in this direction
13700
13701   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13702     return MP_NO_ACTION;        // field has no opening in this direction
13703
13704   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13705   {
13706     SplashAcid(x, y);
13707
13708     Feld[jx][jy] = player->artwork_element;
13709     InitMovingField(jx, jy, MV_DOWN);
13710     Store[jx][jy] = EL_ACID;
13711     ContinueMoving(jx, jy);
13712     BuryPlayer(player);
13713
13714     return MP_DONT_RUN_INTO;
13715   }
13716
13717   if (player_can_move && DONT_RUN_INTO(element))
13718   {
13719     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13720
13721     return MP_DONT_RUN_INTO;
13722   }
13723
13724   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13725     return MP_NO_ACTION;
13726
13727   collect_count = element_info[element].collect_count_initial;
13728
13729   if (!is_player && !IS_COLLECTIBLE(element))   // penguin cannot collect it
13730     return MP_NO_ACTION;
13731
13732   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13733     player_can_move = player_can_move_or_snap;
13734
13735   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13736       game.engine_version >= VERSION_IDENT(2,2,0,0))
13737   {
13738     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13739                                player->index_bit, dig_side);
13740     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13741                                         player->index_bit, dig_side);
13742
13743     if (element == EL_DC_LANDMINE)
13744       Bang(x, y);
13745
13746     if (Feld[x][y] != element)          // field changed by snapping
13747       return MP_ACTION;
13748
13749     return MP_NO_ACTION;
13750   }
13751
13752   if (player->gravity && is_player && !player->is_auto_moving &&
13753       canFallDown(player) && move_direction != MV_DOWN &&
13754       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13755     return MP_NO_ACTION;        // player cannot walk here due to gravity
13756
13757   if (player_can_move &&
13758       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13759   {
13760     int sound_element = SND_ELEMENT(element);
13761     int sound_action = ACTION_WALKING;
13762
13763     if (IS_RND_GATE(element))
13764     {
13765       if (!player->key[RND_GATE_NR(element)])
13766         return MP_NO_ACTION;
13767     }
13768     else if (IS_RND_GATE_GRAY(element))
13769     {
13770       if (!player->key[RND_GATE_GRAY_NR(element)])
13771         return MP_NO_ACTION;
13772     }
13773     else if (IS_RND_GATE_GRAY_ACTIVE(element))
13774     {
13775       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13776         return MP_NO_ACTION;
13777     }
13778     else if (element == EL_EXIT_OPEN ||
13779              element == EL_EM_EXIT_OPEN ||
13780              element == EL_EM_EXIT_OPENING ||
13781              element == EL_STEEL_EXIT_OPEN ||
13782              element == EL_EM_STEEL_EXIT_OPEN ||
13783              element == EL_EM_STEEL_EXIT_OPENING ||
13784              element == EL_SP_EXIT_OPEN ||
13785              element == EL_SP_EXIT_OPENING)
13786     {
13787       sound_action = ACTION_PASSING;    // player is passing exit
13788     }
13789     else if (element == EL_EMPTY)
13790     {
13791       sound_action = ACTION_MOVING;             // nothing to walk on
13792     }
13793
13794     // play sound from background or player, whatever is available
13795     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13796       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13797     else
13798       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13799   }
13800   else if (player_can_move &&
13801            IS_PASSABLE(element) && canPassField(x, y, move_direction))
13802   {
13803     if (!ACCESS_FROM(element, opposite_direction))
13804       return MP_NO_ACTION;      // field not accessible from this direction
13805
13806     if (CAN_MOVE(element))      // only fixed elements can be passed!
13807       return MP_NO_ACTION;
13808
13809     if (IS_EM_GATE(element))
13810     {
13811       if (!player->key[EM_GATE_NR(element)])
13812         return MP_NO_ACTION;
13813     }
13814     else if (IS_EM_GATE_GRAY(element))
13815     {
13816       if (!player->key[EM_GATE_GRAY_NR(element)])
13817         return MP_NO_ACTION;
13818     }
13819     else if (IS_EM_GATE_GRAY_ACTIVE(element))
13820     {
13821       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13822         return MP_NO_ACTION;
13823     }
13824     else if (IS_EMC_GATE(element))
13825     {
13826       if (!player->key[EMC_GATE_NR(element)])
13827         return MP_NO_ACTION;
13828     }
13829     else if (IS_EMC_GATE_GRAY(element))
13830     {
13831       if (!player->key[EMC_GATE_GRAY_NR(element)])
13832         return MP_NO_ACTION;
13833     }
13834     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13835     {
13836       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13837         return MP_NO_ACTION;
13838     }
13839     else if (element == EL_DC_GATE_WHITE ||
13840              element == EL_DC_GATE_WHITE_GRAY ||
13841              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13842     {
13843       if (player->num_white_keys == 0)
13844         return MP_NO_ACTION;
13845
13846       player->num_white_keys--;
13847     }
13848     else if (IS_SP_PORT(element))
13849     {
13850       if (element == EL_SP_GRAVITY_PORT_LEFT ||
13851           element == EL_SP_GRAVITY_PORT_RIGHT ||
13852           element == EL_SP_GRAVITY_PORT_UP ||
13853           element == EL_SP_GRAVITY_PORT_DOWN)
13854         player->gravity = !player->gravity;
13855       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13856                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13857                element == EL_SP_GRAVITY_ON_PORT_UP ||
13858                element == EL_SP_GRAVITY_ON_PORT_DOWN)
13859         player->gravity = TRUE;
13860       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13861                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13862                element == EL_SP_GRAVITY_OFF_PORT_UP ||
13863                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13864         player->gravity = FALSE;
13865     }
13866
13867     // automatically move to the next field with double speed
13868     player->programmed_action = move_direction;
13869
13870     if (player->move_delay_reset_counter == 0)
13871     {
13872       player->move_delay_reset_counter = 2;     // two double speed steps
13873
13874       DOUBLE_PLAYER_SPEED(player);
13875     }
13876
13877     PlayLevelSoundAction(x, y, ACTION_PASSING);
13878   }
13879   else if (player_can_move_or_snap && IS_DIGGABLE(element))
13880   {
13881     RemoveField(x, y);
13882
13883     if (mode != DF_SNAP)
13884     {
13885       GfxElement[x][y] = GFX_ELEMENT(element);
13886       player->is_digging = TRUE;
13887     }
13888
13889     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13890
13891     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13892                                         player->index_bit, dig_side);
13893
13894     if (mode == DF_SNAP)
13895     {
13896       if (level.block_snap_field)
13897         setFieldForSnapping(x, y, element, move_direction);
13898       else
13899         TestIfElementTouchesCustomElement(x, y);        // for empty space
13900
13901       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13902                                           player->index_bit, dig_side);
13903     }
13904   }
13905   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13906   {
13907     RemoveField(x, y);
13908
13909     if (is_player && mode != DF_SNAP)
13910     {
13911       GfxElement[x][y] = element;
13912       player->is_collecting = TRUE;
13913     }
13914
13915     if (element == EL_SPEED_PILL)
13916     {
13917       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13918     }
13919     else if (element == EL_EXTRA_TIME && level.time > 0)
13920     {
13921       TimeLeft += level.extra_time;
13922
13923       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13924
13925       DisplayGameControlValues();
13926     }
13927     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13928     {
13929       player->shield_normal_time_left += level.shield_normal_time;
13930       if (element == EL_SHIELD_DEADLY)
13931         player->shield_deadly_time_left += level.shield_deadly_time;
13932     }
13933     else if (element == EL_DYNAMITE ||
13934              element == EL_EM_DYNAMITE ||
13935              element == EL_SP_DISK_RED)
13936     {
13937       if (player->inventory_size < MAX_INVENTORY_SIZE)
13938         player->inventory_element[player->inventory_size++] = element;
13939
13940       DrawGameDoorValues();
13941     }
13942     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13943     {
13944       player->dynabomb_count++;
13945       player->dynabombs_left++;
13946     }
13947     else if (element == EL_DYNABOMB_INCREASE_SIZE)
13948     {
13949       player->dynabomb_size++;
13950     }
13951     else if (element == EL_DYNABOMB_INCREASE_POWER)
13952     {
13953       player->dynabomb_xl = TRUE;
13954     }
13955     else if (IS_KEY(element))
13956     {
13957       player->key[KEY_NR(element)] = TRUE;
13958
13959       DrawGameDoorValues();
13960     }
13961     else if (element == EL_DC_KEY_WHITE)
13962     {
13963       player->num_white_keys++;
13964
13965       // display white keys?
13966       // DrawGameDoorValues();
13967     }
13968     else if (IS_ENVELOPE(element))
13969     {
13970       player->show_envelope = element;
13971     }
13972     else if (element == EL_EMC_LENSES)
13973     {
13974       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13975
13976       RedrawAllInvisibleElementsForLenses();
13977     }
13978     else if (element == EL_EMC_MAGNIFIER)
13979     {
13980       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13981
13982       RedrawAllInvisibleElementsForMagnifier();
13983     }
13984     else if (IS_DROPPABLE(element) ||
13985              IS_THROWABLE(element))     // can be collected and dropped
13986     {
13987       int i;
13988
13989       if (collect_count == 0)
13990         player->inventory_infinite_element = element;
13991       else
13992         for (i = 0; i < collect_count; i++)
13993           if (player->inventory_size < MAX_INVENTORY_SIZE)
13994             player->inventory_element[player->inventory_size++] = element;
13995
13996       DrawGameDoorValues();
13997     }
13998     else if (collect_count > 0)
13999     {
14000       game.gems_still_needed -= collect_count;
14001       if (game.gems_still_needed < 0)
14002         game.gems_still_needed = 0;
14003
14004       game.snapshot.collected_item = TRUE;
14005
14006       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
14007
14008       DisplayGameControlValues();
14009     }
14010
14011     RaiseScoreElement(element);
14012     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14013
14014     if (is_player)
14015       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14016                                           player->index_bit, dig_side);
14017
14018     if (mode == DF_SNAP)
14019     {
14020       if (level.block_snap_field)
14021         setFieldForSnapping(x, y, element, move_direction);
14022       else
14023         TestIfElementTouchesCustomElement(x, y);        // for empty space
14024
14025       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14026                                           player->index_bit, dig_side);
14027     }
14028   }
14029   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14030   {
14031     if (mode == DF_SNAP && element != EL_BD_ROCK)
14032       return MP_NO_ACTION;
14033
14034     if (CAN_FALL(element) && dy)
14035       return MP_NO_ACTION;
14036
14037     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14038         !(element == EL_SPRING && level.use_spring_bug))
14039       return MP_NO_ACTION;
14040
14041     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14042         ((move_direction & MV_VERTICAL &&
14043           ((element_info[element].move_pattern & MV_LEFT &&
14044             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14045            (element_info[element].move_pattern & MV_RIGHT &&
14046             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14047          (move_direction & MV_HORIZONTAL &&
14048           ((element_info[element].move_pattern & MV_UP &&
14049             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14050            (element_info[element].move_pattern & MV_DOWN &&
14051             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14052       return MP_NO_ACTION;
14053
14054     // do not push elements already moving away faster than player
14055     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14056         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14057       return MP_NO_ACTION;
14058
14059     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14060     {
14061       if (player->push_delay_value == -1 || !player_was_pushing)
14062         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14063     }
14064     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14065     {
14066       if (player->push_delay_value == -1)
14067         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14068     }
14069     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14070     {
14071       if (!player->is_pushing)
14072         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14073     }
14074
14075     player->is_pushing = TRUE;
14076     player->is_active = TRUE;
14077
14078     if (!(IN_LEV_FIELD(nextx, nexty) &&
14079           (IS_FREE(nextx, nexty) ||
14080            (IS_SB_ELEMENT(element) &&
14081             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14082            (IS_CUSTOM_ELEMENT(element) &&
14083             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
14084       return MP_NO_ACTION;
14085
14086     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14087       return MP_NO_ACTION;
14088
14089     if (player->push_delay == -1)       // new pushing; restart delay
14090       player->push_delay = 0;
14091
14092     if (player->push_delay < player->push_delay_value &&
14093         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14094         element != EL_SPRING && element != EL_BALLOON)
14095     {
14096       // make sure that there is no move delay before next try to push
14097       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14098         player->move_delay = 0;
14099
14100       return MP_NO_ACTION;
14101     }
14102
14103     if (IS_CUSTOM_ELEMENT(element) &&
14104         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14105     {
14106       if (!DigFieldByCE(nextx, nexty, element))
14107         return MP_NO_ACTION;
14108     }
14109
14110     if (IS_SB_ELEMENT(element))
14111     {
14112       boolean sokoban_task_solved = FALSE;
14113
14114       if (element == EL_SOKOBAN_FIELD_FULL)
14115       {
14116         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14117
14118         IncrementSokobanFieldsNeeded();
14119         IncrementSokobanObjectsNeeded();
14120       }
14121
14122       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14123       {
14124         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14125
14126         DecrementSokobanFieldsNeeded();
14127         DecrementSokobanObjectsNeeded();
14128
14129         // sokoban object was pushed from empty field to sokoban field
14130         if (Back[x][y] == EL_EMPTY)
14131           sokoban_task_solved = TRUE;
14132       }
14133
14134       Feld[x][y] = EL_SOKOBAN_OBJECT;
14135
14136       if (Back[x][y] == Back[nextx][nexty])
14137         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14138       else if (Back[x][y] != 0)
14139         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14140                                     ACTION_EMPTYING);
14141       else
14142         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14143                                     ACTION_FILLING);
14144
14145       if (sokoban_task_solved &&
14146           game.sokoban_fields_still_needed == 0 &&
14147           game.sokoban_objects_still_needed == 0 &&
14148           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
14149       {
14150         game.players_still_needed = 0;
14151
14152         LevelSolved();
14153
14154         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14155       }
14156     }
14157     else
14158       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14159
14160     InitMovingField(x, y, move_direction);
14161     GfxAction[x][y] = ACTION_PUSHING;
14162
14163     if (mode == DF_SNAP)
14164       ContinueMoving(x, y);
14165     else
14166       MovPos[x][y] = (dx != 0 ? dx : dy);
14167
14168     Pushed[x][y] = TRUE;
14169     Pushed[nextx][nexty] = TRUE;
14170
14171     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14172       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14173     else
14174       player->push_delay_value = -1;    // get new value later
14175
14176     // check for element change _after_ element has been pushed
14177     if (game.use_change_when_pushing_bug)
14178     {
14179       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14180                                  player->index_bit, dig_side);
14181       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14182                                           player->index_bit, dig_side);
14183     }
14184   }
14185   else if (IS_SWITCHABLE(element))
14186   {
14187     if (PLAYER_SWITCHING(player, x, y))
14188     {
14189       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14190                                           player->index_bit, dig_side);
14191
14192       return MP_ACTION;
14193     }
14194
14195     player->is_switching = TRUE;
14196     player->switch_x = x;
14197     player->switch_y = y;
14198
14199     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14200
14201     if (element == EL_ROBOT_WHEEL)
14202     {
14203       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14204
14205       game.robot_wheel_x = x;
14206       game.robot_wheel_y = y;
14207       game.robot_wheel_active = TRUE;
14208
14209       TEST_DrawLevelField(x, y);
14210     }
14211     else if (element == EL_SP_TERMINAL)
14212     {
14213       int xx, yy;
14214
14215       SCAN_PLAYFIELD(xx, yy)
14216       {
14217         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
14218         {
14219           Bang(xx, yy);
14220         }
14221         else if (Feld[xx][yy] == EL_SP_TERMINAL)
14222         {
14223           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14224
14225           ResetGfxAnimation(xx, yy);
14226           TEST_DrawLevelField(xx, yy);
14227         }
14228       }
14229     }
14230     else if (IS_BELT_SWITCH(element))
14231     {
14232       ToggleBeltSwitch(x, y);
14233     }
14234     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14235              element == EL_SWITCHGATE_SWITCH_DOWN ||
14236              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14237              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14238     {
14239       ToggleSwitchgateSwitch(x, y);
14240     }
14241     else if (element == EL_LIGHT_SWITCH ||
14242              element == EL_LIGHT_SWITCH_ACTIVE)
14243     {
14244       ToggleLightSwitch(x, y);
14245     }
14246     else if (element == EL_TIMEGATE_SWITCH ||
14247              element == EL_DC_TIMEGATE_SWITCH)
14248     {
14249       ActivateTimegateSwitch(x, y);
14250     }
14251     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14252              element == EL_BALLOON_SWITCH_RIGHT ||
14253              element == EL_BALLOON_SWITCH_UP    ||
14254              element == EL_BALLOON_SWITCH_DOWN  ||
14255              element == EL_BALLOON_SWITCH_NONE  ||
14256              element == EL_BALLOON_SWITCH_ANY)
14257     {
14258       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14259                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14260                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14261                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14262                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14263                              move_direction);
14264     }
14265     else if (element == EL_LAMP)
14266     {
14267       Feld[x][y] = EL_LAMP_ACTIVE;
14268       game.lights_still_needed--;
14269
14270       ResetGfxAnimation(x, y);
14271       TEST_DrawLevelField(x, y);
14272     }
14273     else if (element == EL_TIME_ORB_FULL)
14274     {
14275       Feld[x][y] = EL_TIME_ORB_EMPTY;
14276
14277       if (level.time > 0 || level.use_time_orb_bug)
14278       {
14279         TimeLeft += level.time_orb_time;
14280         game.no_time_limit = FALSE;
14281
14282         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14283
14284         DisplayGameControlValues();
14285       }
14286
14287       ResetGfxAnimation(x, y);
14288       TEST_DrawLevelField(x, y);
14289     }
14290     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14291              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14292     {
14293       int xx, yy;
14294
14295       game.ball_active = !game.ball_active;
14296
14297       SCAN_PLAYFIELD(xx, yy)
14298       {
14299         int e = Feld[xx][yy];
14300
14301         if (game.ball_active)
14302         {
14303           if (e == EL_EMC_MAGIC_BALL)
14304             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14305           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14306             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14307         }
14308         else
14309         {
14310           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14311             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14312           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14313             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14314         }
14315       }
14316     }
14317
14318     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14319                                         player->index_bit, dig_side);
14320
14321     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14322                                         player->index_bit, dig_side);
14323
14324     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14325                                         player->index_bit, dig_side);
14326
14327     return MP_ACTION;
14328   }
14329   else
14330   {
14331     if (!PLAYER_SWITCHING(player, x, y))
14332     {
14333       player->is_switching = TRUE;
14334       player->switch_x = x;
14335       player->switch_y = y;
14336
14337       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14338                                  player->index_bit, dig_side);
14339       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14340                                           player->index_bit, dig_side);
14341
14342       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14343                                  player->index_bit, dig_side);
14344       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14345                                           player->index_bit, dig_side);
14346     }
14347
14348     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14349                                player->index_bit, dig_side);
14350     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14351                                         player->index_bit, dig_side);
14352
14353     return MP_NO_ACTION;
14354   }
14355
14356   player->push_delay = -1;
14357
14358   if (is_player)                // function can also be called by EL_PENGUIN
14359   {
14360     if (Feld[x][y] != element)          // really digged/collected something
14361     {
14362       player->is_collecting = !player->is_digging;
14363       player->is_active = TRUE;
14364     }
14365   }
14366
14367   return MP_MOVING;
14368 }
14369
14370 static boolean DigFieldByCE(int x, int y, int digging_element)
14371 {
14372   int element = Feld[x][y];
14373
14374   if (!IS_FREE(x, y))
14375   {
14376     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14377                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14378                   ACTION_BREAKING);
14379
14380     // no element can dig solid indestructible elements
14381     if (IS_INDESTRUCTIBLE(element) &&
14382         !IS_DIGGABLE(element) &&
14383         !IS_COLLECTIBLE(element))
14384       return FALSE;
14385
14386     if (AmoebaNr[x][y] &&
14387         (element == EL_AMOEBA_FULL ||
14388          element == EL_BD_AMOEBA ||
14389          element == EL_AMOEBA_GROWING))
14390     {
14391       AmoebaCnt[AmoebaNr[x][y]]--;
14392       AmoebaCnt2[AmoebaNr[x][y]]--;
14393     }
14394
14395     if (IS_MOVING(x, y))
14396       RemoveMovingField(x, y);
14397     else
14398     {
14399       RemoveField(x, y);
14400       TEST_DrawLevelField(x, y);
14401     }
14402
14403     // if digged element was about to explode, prevent the explosion
14404     ExplodeField[x][y] = EX_TYPE_NONE;
14405
14406     PlayLevelSoundAction(x, y, action);
14407   }
14408
14409   Store[x][y] = EL_EMPTY;
14410
14411   // this makes it possible to leave the removed element again
14412   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14413     Store[x][y] = element;
14414
14415   return TRUE;
14416 }
14417
14418 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14419 {
14420   int jx = player->jx, jy = player->jy;
14421   int x = jx + dx, y = jy + dy;
14422   int snap_direction = (dx == -1 ? MV_LEFT  :
14423                         dx == +1 ? MV_RIGHT :
14424                         dy == -1 ? MV_UP    :
14425                         dy == +1 ? MV_DOWN  : MV_NONE);
14426   boolean can_continue_snapping = (level.continuous_snapping &&
14427                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14428
14429   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14430     return FALSE;
14431
14432   if (!player->active || !IN_LEV_FIELD(x, y))
14433     return FALSE;
14434
14435   if (dx && dy)
14436     return FALSE;
14437
14438   if (!dx && !dy)
14439   {
14440     if (player->MovPos == 0)
14441       player->is_pushing = FALSE;
14442
14443     player->is_snapping = FALSE;
14444
14445     if (player->MovPos == 0)
14446     {
14447       player->is_moving = FALSE;
14448       player->is_digging = FALSE;
14449       player->is_collecting = FALSE;
14450     }
14451
14452     return FALSE;
14453   }
14454
14455   // prevent snapping with already pressed snap key when not allowed
14456   if (player->is_snapping && !can_continue_snapping)
14457     return FALSE;
14458
14459   player->MovDir = snap_direction;
14460
14461   if (player->MovPos == 0)
14462   {
14463     player->is_moving = FALSE;
14464     player->is_digging = FALSE;
14465     player->is_collecting = FALSE;
14466   }
14467
14468   player->is_dropping = FALSE;
14469   player->is_dropping_pressed = FALSE;
14470   player->drop_pressed_delay = 0;
14471
14472   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14473     return FALSE;
14474
14475   player->is_snapping = TRUE;
14476   player->is_active = TRUE;
14477
14478   if (player->MovPos == 0)
14479   {
14480     player->is_moving = FALSE;
14481     player->is_digging = FALSE;
14482     player->is_collecting = FALSE;
14483   }
14484
14485   if (player->MovPos != 0)      // prevent graphic bugs in versions < 2.2.0
14486     TEST_DrawLevelField(player->last_jx, player->last_jy);
14487
14488   TEST_DrawLevelField(x, y);
14489
14490   return TRUE;
14491 }
14492
14493 static boolean DropElement(struct PlayerInfo *player)
14494 {
14495   int old_element, new_element;
14496   int dropx = player->jx, dropy = player->jy;
14497   int drop_direction = player->MovDir;
14498   int drop_side = drop_direction;
14499   int drop_element = get_next_dropped_element(player);
14500
14501   /* do not drop an element on top of another element; when holding drop key
14502      pressed without moving, dropped element must move away before the next
14503      element can be dropped (this is especially important if the next element
14504      is dynamite, which can be placed on background for historical reasons) */
14505   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14506     return MP_ACTION;
14507
14508   if (IS_THROWABLE(drop_element))
14509   {
14510     dropx += GET_DX_FROM_DIR(drop_direction);
14511     dropy += GET_DY_FROM_DIR(drop_direction);
14512
14513     if (!IN_LEV_FIELD(dropx, dropy))
14514       return FALSE;
14515   }
14516
14517   old_element = Feld[dropx][dropy];     // old element at dropping position
14518   new_element = drop_element;           // default: no change when dropping
14519
14520   // check if player is active, not moving and ready to drop
14521   if (!player->active || player->MovPos || player->drop_delay > 0)
14522     return FALSE;
14523
14524   // check if player has anything that can be dropped
14525   if (new_element == EL_UNDEFINED)
14526     return FALSE;
14527
14528   // only set if player has anything that can be dropped
14529   player->is_dropping_pressed = TRUE;
14530
14531   // check if drop key was pressed long enough for EM style dynamite
14532   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14533     return FALSE;
14534
14535   // check if anything can be dropped at the current position
14536   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14537     return FALSE;
14538
14539   // collected custom elements can only be dropped on empty fields
14540   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14541     return FALSE;
14542
14543   if (old_element != EL_EMPTY)
14544     Back[dropx][dropy] = old_element;   // store old element on this field
14545
14546   ResetGfxAnimation(dropx, dropy);
14547   ResetRandomAnimationValue(dropx, dropy);
14548
14549   if (player->inventory_size > 0 ||
14550       player->inventory_infinite_element != EL_UNDEFINED)
14551   {
14552     if (player->inventory_size > 0)
14553     {
14554       player->inventory_size--;
14555
14556       DrawGameDoorValues();
14557
14558       if (new_element == EL_DYNAMITE)
14559         new_element = EL_DYNAMITE_ACTIVE;
14560       else if (new_element == EL_EM_DYNAMITE)
14561         new_element = EL_EM_DYNAMITE_ACTIVE;
14562       else if (new_element == EL_SP_DISK_RED)
14563         new_element = EL_SP_DISK_RED_ACTIVE;
14564     }
14565
14566     Feld[dropx][dropy] = new_element;
14567
14568     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14569       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14570                           el2img(Feld[dropx][dropy]), 0);
14571
14572     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14573
14574     // needed if previous element just changed to "empty" in the last frame
14575     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14576
14577     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14578                                player->index_bit, drop_side);
14579     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14580                                         CE_PLAYER_DROPS_X,
14581                                         player->index_bit, drop_side);
14582
14583     TestIfElementTouchesCustomElement(dropx, dropy);
14584   }
14585   else          // player is dropping a dyna bomb
14586   {
14587     player->dynabombs_left--;
14588
14589     Feld[dropx][dropy] = new_element;
14590
14591     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14592       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14593                           el2img(Feld[dropx][dropy]), 0);
14594
14595     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14596   }
14597
14598   if (Feld[dropx][dropy] == new_element) // uninitialized unless CE change
14599     InitField_WithBug1(dropx, dropy, FALSE);
14600
14601   new_element = Feld[dropx][dropy];     // element might have changed
14602
14603   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14604       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14605   {
14606     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14607       MovDir[dropx][dropy] = drop_direction;
14608
14609     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14610
14611     // do not cause impact style collision by dropping elements that can fall
14612     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14613   }
14614
14615   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14616   player->is_dropping = TRUE;
14617
14618   player->drop_pressed_delay = 0;
14619   player->is_dropping_pressed = FALSE;
14620
14621   player->drop_x = dropx;
14622   player->drop_y = dropy;
14623
14624   return TRUE;
14625 }
14626
14627 // ----------------------------------------------------------------------------
14628 // game sound playing functions
14629 // ----------------------------------------------------------------------------
14630
14631 static int *loop_sound_frame = NULL;
14632 static int *loop_sound_volume = NULL;
14633
14634 void InitPlayLevelSound(void)
14635 {
14636   int num_sounds = getSoundListSize();
14637
14638   checked_free(loop_sound_frame);
14639   checked_free(loop_sound_volume);
14640
14641   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14642   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14643 }
14644
14645 static void PlayLevelSound(int x, int y, int nr)
14646 {
14647   int sx = SCREENX(x), sy = SCREENY(y);
14648   int volume, stereo_position;
14649   int max_distance = 8;
14650   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14651
14652   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14653       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14654     return;
14655
14656   if (!IN_LEV_FIELD(x, y) ||
14657       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14658       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14659     return;
14660
14661   volume = SOUND_MAX_VOLUME;
14662
14663   if (!IN_SCR_FIELD(sx, sy))
14664   {
14665     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14666     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14667
14668     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14669   }
14670
14671   stereo_position = (SOUND_MAX_LEFT +
14672                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14673                      (SCR_FIELDX + 2 * max_distance));
14674
14675   if (IS_LOOP_SOUND(nr))
14676   {
14677     /* This assures that quieter loop sounds do not overwrite louder ones,
14678        while restarting sound volume comparison with each new game frame. */
14679
14680     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14681       return;
14682
14683     loop_sound_volume[nr] = volume;
14684     loop_sound_frame[nr] = FrameCounter;
14685   }
14686
14687   PlaySoundExt(nr, volume, stereo_position, type);
14688 }
14689
14690 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14691 {
14692   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14693                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14694                  y < LEVELY(BY1) ? LEVELY(BY1) :
14695                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14696                  sound_action);
14697 }
14698
14699 static void PlayLevelSoundAction(int x, int y, int action)
14700 {
14701   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14702 }
14703
14704 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14705 {
14706   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14707
14708   if (sound_effect != SND_UNDEFINED)
14709     PlayLevelSound(x, y, sound_effect);
14710 }
14711
14712 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14713                                               int action)
14714 {
14715   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14716
14717   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14718     PlayLevelSound(x, y, sound_effect);
14719 }
14720
14721 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14722 {
14723   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14724
14725   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14726     PlayLevelSound(x, y, sound_effect);
14727 }
14728
14729 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14730 {
14731   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14732
14733   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14734     StopSound(sound_effect);
14735 }
14736
14737 static int getLevelMusicNr(void)
14738 {
14739   if (levelset.music[level_nr] != MUS_UNDEFINED)
14740     return levelset.music[level_nr];            // from config file
14741   else
14742     return MAP_NOCONF_MUSIC(level_nr);          // from music dir
14743 }
14744
14745 static void FadeLevelSounds(void)
14746 {
14747   FadeSounds();
14748 }
14749
14750 static void FadeLevelMusic(void)
14751 {
14752   int music_nr = getLevelMusicNr();
14753   char *curr_music = getCurrentlyPlayingMusicFilename();
14754   char *next_music = getMusicInfoEntryFilename(music_nr);
14755
14756   if (!strEqual(curr_music, next_music))
14757     FadeMusic();
14758 }
14759
14760 void FadeLevelSoundsAndMusic(void)
14761 {
14762   FadeLevelSounds();
14763   FadeLevelMusic();
14764 }
14765
14766 static void PlayLevelMusic(void)
14767 {
14768   int music_nr = getLevelMusicNr();
14769   char *curr_music = getCurrentlyPlayingMusicFilename();
14770   char *next_music = getMusicInfoEntryFilename(music_nr);
14771
14772   if (!strEqual(curr_music, next_music))
14773     PlayMusicLoop(music_nr);
14774 }
14775
14776 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14777 {
14778   int element = (element_em > -1 ? map_element_EM_to_RND_game(element_em) : 0);
14779   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14780   int x = xx - 1 - offset;
14781   int y = yy - 1 - offset;
14782
14783   switch (sample)
14784   {
14785     case SOUND_blank:
14786       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14787       break;
14788
14789     case SOUND_roll:
14790       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14791       break;
14792
14793     case SOUND_stone:
14794       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14795       break;
14796
14797     case SOUND_nut:
14798       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14799       break;
14800
14801     case SOUND_crack:
14802       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14803       break;
14804
14805     case SOUND_bug:
14806       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14807       break;
14808
14809     case SOUND_tank:
14810       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14811       break;
14812
14813     case SOUND_android_clone:
14814       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14815       break;
14816
14817     case SOUND_android_move:
14818       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14819       break;
14820
14821     case SOUND_spring:
14822       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14823       break;
14824
14825     case SOUND_slurp:
14826       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14827       break;
14828
14829     case SOUND_eater:
14830       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14831       break;
14832
14833     case SOUND_eater_eat:
14834       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14835       break;
14836
14837     case SOUND_alien:
14838       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14839       break;
14840
14841     case SOUND_collect:
14842       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14843       break;
14844
14845     case SOUND_diamond:
14846       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14847       break;
14848
14849     case SOUND_squash:
14850       // !!! CHECK THIS !!!
14851 #if 1
14852       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14853 #else
14854       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14855 #endif
14856       break;
14857
14858     case SOUND_wonderfall:
14859       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14860       break;
14861
14862     case SOUND_drip:
14863       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14864       break;
14865
14866     case SOUND_push:
14867       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14868       break;
14869
14870     case SOUND_dirt:
14871       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14872       break;
14873
14874     case SOUND_acid:
14875       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14876       break;
14877
14878     case SOUND_ball:
14879       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14880       break;
14881
14882     case SOUND_slide:
14883       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14884       break;
14885
14886     case SOUND_wonder:
14887       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14888       break;
14889
14890     case SOUND_door:
14891       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14892       break;
14893
14894     case SOUND_exit_open:
14895       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14896       break;
14897
14898     case SOUND_exit_leave:
14899       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14900       break;
14901
14902     case SOUND_dynamite:
14903       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14904       break;
14905
14906     case SOUND_tick:
14907       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14908       break;
14909
14910     case SOUND_press:
14911       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14912       break;
14913
14914     case SOUND_wheel:
14915       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14916       break;
14917
14918     case SOUND_boom:
14919       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14920       break;
14921
14922     case SOUND_die:
14923       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14924       break;
14925
14926     case SOUND_time:
14927       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14928       break;
14929
14930     default:
14931       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14932       break;
14933   }
14934 }
14935
14936 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
14937 {
14938   int element = map_element_SP_to_RND(element_sp);
14939   int action = map_action_SP_to_RND(action_sp);
14940   int offset = (setup.sp_show_border_elements ? 0 : 1);
14941   int x = xx - offset;
14942   int y = yy - offset;
14943
14944   PlayLevelSoundElementAction(x, y, element, action);
14945 }
14946
14947 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
14948 {
14949   int element = map_element_MM_to_RND(element_mm);
14950   int action = map_action_MM_to_RND(action_mm);
14951   int offset = 0;
14952   int x = xx - offset;
14953   int y = yy - offset;
14954
14955   if (!IS_MM_ELEMENT(element))
14956     element = EL_MM_DEFAULT;
14957
14958   PlayLevelSoundElementAction(x, y, element, action);
14959 }
14960
14961 void PlaySound_MM(int sound_mm)
14962 {
14963   int sound = map_sound_MM_to_RND(sound_mm);
14964
14965   if (sound == SND_UNDEFINED)
14966     return;
14967
14968   PlaySound(sound);
14969 }
14970
14971 void PlaySoundLoop_MM(int sound_mm)
14972 {
14973   int sound = map_sound_MM_to_RND(sound_mm);
14974
14975   if (sound == SND_UNDEFINED)
14976     return;
14977
14978   PlaySoundLoop(sound);
14979 }
14980
14981 void StopSound_MM(int sound_mm)
14982 {
14983   int sound = map_sound_MM_to_RND(sound_mm);
14984
14985   if (sound == SND_UNDEFINED)
14986     return;
14987
14988   StopSound(sound);
14989 }
14990
14991 void RaiseScore(int value)
14992 {
14993   game.score += value;
14994
14995   game_panel_controls[GAME_PANEL_SCORE].value = game.score;
14996
14997   DisplayGameControlValues();
14998 }
14999
15000 void RaiseScoreElement(int element)
15001 {
15002   switch (element)
15003   {
15004     case EL_EMERALD:
15005     case EL_BD_DIAMOND:
15006     case EL_EMERALD_YELLOW:
15007     case EL_EMERALD_RED:
15008     case EL_EMERALD_PURPLE:
15009     case EL_SP_INFOTRON:
15010       RaiseScore(level.score[SC_EMERALD]);
15011       break;
15012     case EL_DIAMOND:
15013       RaiseScore(level.score[SC_DIAMOND]);
15014       break;
15015     case EL_CRYSTAL:
15016       RaiseScore(level.score[SC_CRYSTAL]);
15017       break;
15018     case EL_PEARL:
15019       RaiseScore(level.score[SC_PEARL]);
15020       break;
15021     case EL_BUG:
15022     case EL_BD_BUTTERFLY:
15023     case EL_SP_ELECTRON:
15024       RaiseScore(level.score[SC_BUG]);
15025       break;
15026     case EL_SPACESHIP:
15027     case EL_BD_FIREFLY:
15028     case EL_SP_SNIKSNAK:
15029       RaiseScore(level.score[SC_SPACESHIP]);
15030       break;
15031     case EL_YAMYAM:
15032     case EL_DARK_YAMYAM:
15033       RaiseScore(level.score[SC_YAMYAM]);
15034       break;
15035     case EL_ROBOT:
15036       RaiseScore(level.score[SC_ROBOT]);
15037       break;
15038     case EL_PACMAN:
15039       RaiseScore(level.score[SC_PACMAN]);
15040       break;
15041     case EL_NUT:
15042       RaiseScore(level.score[SC_NUT]);
15043       break;
15044     case EL_DYNAMITE:
15045     case EL_EM_DYNAMITE:
15046     case EL_SP_DISK_RED:
15047     case EL_DYNABOMB_INCREASE_NUMBER:
15048     case EL_DYNABOMB_INCREASE_SIZE:
15049     case EL_DYNABOMB_INCREASE_POWER:
15050       RaiseScore(level.score[SC_DYNAMITE]);
15051       break;
15052     case EL_SHIELD_NORMAL:
15053     case EL_SHIELD_DEADLY:
15054       RaiseScore(level.score[SC_SHIELD]);
15055       break;
15056     case EL_EXTRA_TIME:
15057       RaiseScore(level.extra_time_score);
15058       break;
15059     case EL_KEY_1:
15060     case EL_KEY_2:
15061     case EL_KEY_3:
15062     case EL_KEY_4:
15063     case EL_EM_KEY_1:
15064     case EL_EM_KEY_2:
15065     case EL_EM_KEY_3:
15066     case EL_EM_KEY_4:
15067     case EL_EMC_KEY_5:
15068     case EL_EMC_KEY_6:
15069     case EL_EMC_KEY_7:
15070     case EL_EMC_KEY_8:
15071     case EL_DC_KEY_WHITE:
15072       RaiseScore(level.score[SC_KEY]);
15073       break;
15074     default:
15075       RaiseScore(element_info[element].collect_score);
15076       break;
15077   }
15078 }
15079
15080 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15081 {
15082   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15083   {
15084     // closing door required in case of envelope style request dialogs
15085     if (!skip_request)
15086     {
15087       // prevent short reactivation of overlay buttons while closing door
15088       SetOverlayActive(FALSE);
15089
15090       CloseDoor(DOOR_CLOSE_1);
15091     }
15092
15093     if (network.enabled)
15094       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15095     else
15096     {
15097       if (quick_quit)
15098         FadeSkipNextFadeIn();
15099
15100       SetGameStatus(GAME_MODE_MAIN);
15101
15102       DrawMainMenu();
15103     }
15104   }
15105   else          // continue playing the game
15106   {
15107     if (tape.playing && tape.deactivate_display)
15108       TapeDeactivateDisplayOff(TRUE);
15109
15110     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15111
15112     if (tape.playing && tape.deactivate_display)
15113       TapeDeactivateDisplayOn();
15114   }
15115 }
15116
15117 void RequestQuitGame(boolean ask_if_really_quit)
15118 {
15119   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
15120   boolean skip_request = game.all_players_gone || quick_quit;
15121
15122   RequestQuitGameExt(skip_request, quick_quit,
15123                      "Do you really want to quit the game?");
15124 }
15125
15126 void RequestRestartGame(char *message)
15127 {
15128   game.restart_game_message = NULL;
15129
15130   boolean has_started_game = hasStartedNetworkGame();
15131   int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM);
15132
15133   if (Request(message, request_mode | REQ_STAY_CLOSED) && has_started_game)
15134   {
15135     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15136   }
15137   else
15138   {
15139     SetGameStatus(GAME_MODE_MAIN);
15140
15141     DrawMainMenu();
15142   }
15143 }
15144
15145 void CheckGameOver(void)
15146 {
15147   static boolean last_game_over = FALSE;
15148   static int game_over_delay = 0;
15149   int game_over_delay_value = 50;
15150   boolean game_over = checkGameFailed();
15151
15152   // do not handle game over if request dialog is already active
15153   if (game.request_active)
15154     return;
15155
15156   // do not ask to play again if game was never actually played
15157   if (!game.GamePlayed)
15158     return;
15159
15160   if (!game_over)
15161   {
15162     last_game_over = FALSE;
15163     game_over_delay = game_over_delay_value;
15164
15165     return;
15166   }
15167
15168   if (game_over_delay > 0)
15169   {
15170     game_over_delay--;
15171
15172     return;
15173   }
15174
15175   if (last_game_over != game_over)
15176     game.restart_game_message = (hasStartedNetworkGame() ?
15177                                  "Game over! Play it again?" :
15178                                  "Game over!");
15179
15180   last_game_over = game_over;
15181 }
15182
15183 boolean checkGameSolved(void)
15184 {
15185   // set for all game engines if level was solved
15186   return game.LevelSolved_GameEnd;
15187 }
15188
15189 boolean checkGameFailed(void)
15190 {
15191   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15192     return (game_em.game_over && !game_em.level_solved);
15193   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15194     return (game_sp.game_over && !game_sp.level_solved);
15195   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15196     return (game_mm.game_over && !game_mm.level_solved);
15197   else                          // GAME_ENGINE_TYPE_RND
15198     return (game.GameOver && !game.LevelSolved);
15199 }
15200
15201 boolean checkGameEnded(void)
15202 {
15203   return (checkGameSolved() || checkGameFailed());
15204 }
15205
15206
15207 // ----------------------------------------------------------------------------
15208 // random generator functions
15209 // ----------------------------------------------------------------------------
15210
15211 unsigned int InitEngineRandom_RND(int seed)
15212 {
15213   game.num_random_calls = 0;
15214
15215   return InitEngineRandom(seed);
15216 }
15217
15218 unsigned int RND(int max)
15219 {
15220   if (max > 0)
15221   {
15222     game.num_random_calls++;
15223
15224     return GetEngineRandom(max);
15225   }
15226
15227   return 0;
15228 }
15229
15230
15231 // ----------------------------------------------------------------------------
15232 // game engine snapshot handling functions
15233 // ----------------------------------------------------------------------------
15234
15235 struct EngineSnapshotInfo
15236 {
15237   // runtime values for custom element collect score
15238   int collect_score[NUM_CUSTOM_ELEMENTS];
15239
15240   // runtime values for group element choice position
15241   int choice_pos[NUM_GROUP_ELEMENTS];
15242
15243   // runtime values for belt position animations
15244   int belt_graphic[4][NUM_BELT_PARTS];
15245   int belt_anim_mode[4][NUM_BELT_PARTS];
15246 };
15247
15248 static struct EngineSnapshotInfo engine_snapshot_rnd;
15249 static char *snapshot_level_identifier = NULL;
15250 static int snapshot_level_nr = -1;
15251
15252 static void SaveEngineSnapshotValues_RND(void)
15253 {
15254   static int belt_base_active_element[4] =
15255   {
15256     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15257     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15258     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15259     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15260   };
15261   int i, j;
15262
15263   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15264   {
15265     int element = EL_CUSTOM_START + i;
15266
15267     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15268   }
15269
15270   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15271   {
15272     int element = EL_GROUP_START + i;
15273
15274     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15275   }
15276
15277   for (i = 0; i < 4; i++)
15278   {
15279     for (j = 0; j < NUM_BELT_PARTS; j++)
15280     {
15281       int element = belt_base_active_element[i] + j;
15282       int graphic = el2img(element);
15283       int anim_mode = graphic_info[graphic].anim_mode;
15284
15285       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15286       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15287     }
15288   }
15289 }
15290
15291 static void LoadEngineSnapshotValues_RND(void)
15292 {
15293   unsigned int num_random_calls = game.num_random_calls;
15294   int i, j;
15295
15296   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15297   {
15298     int element = EL_CUSTOM_START + i;
15299
15300     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15301   }
15302
15303   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15304   {
15305     int element = EL_GROUP_START + i;
15306
15307     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15308   }
15309
15310   for (i = 0; i < 4; i++)
15311   {
15312     for (j = 0; j < NUM_BELT_PARTS; j++)
15313     {
15314       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15315       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15316
15317       graphic_info[graphic].anim_mode = anim_mode;
15318     }
15319   }
15320
15321   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15322   {
15323     InitRND(tape.random_seed);
15324     for (i = 0; i < num_random_calls; i++)
15325       RND(1);
15326   }
15327
15328   if (game.num_random_calls != num_random_calls)
15329   {
15330     Error(ERR_INFO, "number of random calls out of sync");
15331     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
15332     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
15333     Error(ERR_EXIT, "this should not happen -- please debug");
15334   }
15335 }
15336
15337 void FreeEngineSnapshotSingle(void)
15338 {
15339   FreeSnapshotSingle();
15340
15341   setString(&snapshot_level_identifier, NULL);
15342   snapshot_level_nr = -1;
15343 }
15344
15345 void FreeEngineSnapshotList(void)
15346 {
15347   FreeSnapshotList();
15348 }
15349
15350 static ListNode *SaveEngineSnapshotBuffers(void)
15351 {
15352   ListNode *buffers = NULL;
15353
15354   // copy some special values to a structure better suited for the snapshot
15355
15356   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15357     SaveEngineSnapshotValues_RND();
15358   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15359     SaveEngineSnapshotValues_EM();
15360   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15361     SaveEngineSnapshotValues_SP(&buffers);
15362   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15363     SaveEngineSnapshotValues_MM(&buffers);
15364
15365   // save values stored in special snapshot structure
15366
15367   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15368     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15369   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15370     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15371   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15372     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15373   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15374     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15375
15376   // save further RND engine values
15377
15378   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15379   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15380   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15381
15382   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15383   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15384   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15385   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15386   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15387
15388   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15389   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15390   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15391
15392   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15393
15394   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15395   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15396
15397   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Feld));
15398   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15399   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15400   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15401   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15402   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15403   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15404   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15405   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15406   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15407   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15408   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15409   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15410   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15411   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15412   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15413   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15414   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15415
15416   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15417   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15418
15419   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15420   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15421   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15422
15423   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15424   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15425
15426   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15427   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15428   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15429   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15430   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15431
15432   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15433   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15434
15435 #if 0
15436   ListNode *node = engine_snapshot_list_rnd;
15437   int num_bytes = 0;
15438
15439   while (node != NULL)
15440   {
15441     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15442
15443     node = node->next;
15444   }
15445
15446   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
15447 #endif
15448
15449   return buffers;
15450 }
15451
15452 void SaveEngineSnapshotSingle(void)
15453 {
15454   ListNode *buffers = SaveEngineSnapshotBuffers();
15455
15456   // finally save all snapshot buffers to single snapshot
15457   SaveSnapshotSingle(buffers);
15458
15459   // save level identification information
15460   setString(&snapshot_level_identifier, leveldir_current->identifier);
15461   snapshot_level_nr = level_nr;
15462 }
15463
15464 boolean CheckSaveEngineSnapshotToList(void)
15465 {
15466   boolean save_snapshot =
15467     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
15468      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
15469       game.snapshot.changed_action) ||
15470      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15471       game.snapshot.collected_item));
15472
15473   game.snapshot.changed_action = FALSE;
15474   game.snapshot.collected_item = FALSE;
15475   game.snapshot.save_snapshot = save_snapshot;
15476
15477   return save_snapshot;
15478 }
15479
15480 void SaveEngineSnapshotToList(void)
15481 {
15482   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
15483       tape.quick_resume)
15484     return;
15485
15486   ListNode *buffers = SaveEngineSnapshotBuffers();
15487
15488   // finally save all snapshot buffers to snapshot list
15489   SaveSnapshotToList(buffers);
15490 }
15491
15492 void SaveEngineSnapshotToListInitial(void)
15493 {
15494   FreeEngineSnapshotList();
15495
15496   SaveEngineSnapshotToList();
15497 }
15498
15499 static void LoadEngineSnapshotValues(void)
15500 {
15501   // restore special values from snapshot structure
15502
15503   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15504     LoadEngineSnapshotValues_RND();
15505   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15506     LoadEngineSnapshotValues_EM();
15507   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15508     LoadEngineSnapshotValues_SP();
15509   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15510     LoadEngineSnapshotValues_MM();
15511 }
15512
15513 void LoadEngineSnapshotSingle(void)
15514 {
15515   LoadSnapshotSingle();
15516
15517   LoadEngineSnapshotValues();
15518 }
15519
15520 static void LoadEngineSnapshot_Undo(int steps)
15521 {
15522   LoadSnapshotFromList_Older(steps);
15523
15524   LoadEngineSnapshotValues();
15525 }
15526
15527 static void LoadEngineSnapshot_Redo(int steps)
15528 {
15529   LoadSnapshotFromList_Newer(steps);
15530
15531   LoadEngineSnapshotValues();
15532 }
15533
15534 boolean CheckEngineSnapshotSingle(void)
15535 {
15536   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15537           snapshot_level_nr == level_nr);
15538 }
15539
15540 boolean CheckEngineSnapshotList(void)
15541 {
15542   return CheckSnapshotList();
15543 }
15544
15545
15546 // ---------- new game button stuff -------------------------------------------
15547
15548 static struct
15549 {
15550   int graphic;
15551   struct XY *pos;
15552   int gadget_id;
15553   boolean *setup_value;
15554   boolean allowed_on_tape;
15555   boolean is_touch_button;
15556   char *infotext;
15557 } gamebutton_info[NUM_GAME_BUTTONS] =
15558 {
15559   {
15560     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
15561     GAME_CTRL_ID_STOP,                          NULL,
15562     TRUE, FALSE,                                "stop game"
15563   },
15564   {
15565     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
15566     GAME_CTRL_ID_PAUSE,                         NULL,
15567     TRUE, FALSE,                                "pause game"
15568   },
15569   {
15570     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
15571     GAME_CTRL_ID_PLAY,                          NULL,
15572     TRUE, FALSE,                                "play game"
15573   },
15574   {
15575     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
15576     GAME_CTRL_ID_UNDO,                          NULL,
15577     TRUE, FALSE,                                "undo step"
15578   },
15579   {
15580     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
15581     GAME_CTRL_ID_REDO,                          NULL,
15582     TRUE, FALSE,                                "redo step"
15583   },
15584   {
15585     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
15586     GAME_CTRL_ID_SAVE,                          NULL,
15587     TRUE, FALSE,                                "save game"
15588   },
15589   {
15590     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
15591     GAME_CTRL_ID_PAUSE2,                        NULL,
15592     TRUE, FALSE,                                "pause game"
15593   },
15594   {
15595     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
15596     GAME_CTRL_ID_LOAD,                          NULL,
15597     TRUE, FALSE,                                "load game"
15598   },
15599   {
15600     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
15601     GAME_CTRL_ID_PANEL_STOP,                    NULL,
15602     FALSE, FALSE,                               "stop game"
15603   },
15604   {
15605     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
15606     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
15607     FALSE, FALSE,                               "pause game"
15608   },
15609   {
15610     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
15611     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
15612     FALSE, FALSE,                               "play game"
15613   },
15614   {
15615     IMG_GFX_GAME_BUTTON_TOUCH_STOP,             &game.button.touch_stop,
15616     GAME_CTRL_ID_TOUCH_STOP,                    NULL,
15617     FALSE, TRUE,                                "stop game"
15618   },
15619   {
15620     IMG_GFX_GAME_BUTTON_TOUCH_PAUSE,            &game.button.touch_pause,
15621     GAME_CTRL_ID_TOUCH_PAUSE,                   NULL,
15622     FALSE, TRUE,                                "pause game"
15623   },
15624   {
15625     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
15626     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
15627     TRUE, FALSE,                                "background music on/off"
15628   },
15629   {
15630     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
15631     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
15632     TRUE, FALSE,                                "sound loops on/off"
15633   },
15634   {
15635     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
15636     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
15637     TRUE, FALSE,                                "normal sounds on/off"
15638   },
15639   {
15640     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
15641     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
15642     FALSE, FALSE,                               "background music on/off"
15643   },
15644   {
15645     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
15646     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
15647     FALSE, FALSE,                               "sound loops on/off"
15648   },
15649   {
15650     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
15651     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
15652     FALSE, FALSE,                               "normal sounds on/off"
15653   }
15654 };
15655
15656 void CreateGameButtons(void)
15657 {
15658   int i;
15659
15660   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15661   {
15662     int graphic = gamebutton_info[i].graphic;
15663     struct GraphicInfo *gfx = &graphic_info[graphic];
15664     struct XY *pos = gamebutton_info[i].pos;
15665     struct GadgetInfo *gi;
15666     int button_type;
15667     boolean checked;
15668     unsigned int event_mask;
15669     boolean is_touch_button = gamebutton_info[i].is_touch_button;
15670     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
15671     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
15672     int base_x = (is_touch_button ? 0 : on_tape ? VX : DX);
15673     int base_y = (is_touch_button ? 0 : on_tape ? VY : DY);
15674     int gd_x   = gfx->src_x;
15675     int gd_y   = gfx->src_y;
15676     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
15677     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
15678     int gd_xa  = gfx->src_x + gfx->active_xoffset;
15679     int gd_ya  = gfx->src_y + gfx->active_yoffset;
15680     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
15681     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
15682     int x = (is_touch_button ? pos->x : GDI_ACTIVE_POS(pos->x));
15683     int y = (is_touch_button ? pos->y : GDI_ACTIVE_POS(pos->y));
15684     int id = i;
15685
15686     if (gfx->bitmap == NULL)
15687     {
15688       game_gadget[id] = NULL;
15689
15690       continue;
15691     }
15692
15693     if (id == GAME_CTRL_ID_STOP ||
15694         id == GAME_CTRL_ID_PANEL_STOP ||
15695         id == GAME_CTRL_ID_TOUCH_STOP ||
15696         id == GAME_CTRL_ID_PLAY ||
15697         id == GAME_CTRL_ID_PANEL_PLAY ||
15698         id == GAME_CTRL_ID_SAVE ||
15699         id == GAME_CTRL_ID_LOAD)
15700     {
15701       button_type = GD_TYPE_NORMAL_BUTTON;
15702       checked = FALSE;
15703       event_mask = GD_EVENT_RELEASED;
15704     }
15705     else if (id == GAME_CTRL_ID_UNDO ||
15706              id == GAME_CTRL_ID_REDO)
15707     {
15708       button_type = GD_TYPE_NORMAL_BUTTON;
15709       checked = FALSE;
15710       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
15711     }
15712     else
15713     {
15714       button_type = GD_TYPE_CHECK_BUTTON;
15715       checked = (gamebutton_info[i].setup_value != NULL ?
15716                  *gamebutton_info[i].setup_value : FALSE);
15717       event_mask = GD_EVENT_PRESSED;
15718     }
15719
15720     gi = CreateGadget(GDI_CUSTOM_ID, id,
15721                       GDI_IMAGE_ID, graphic,
15722                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
15723                       GDI_X, base_x + x,
15724                       GDI_Y, base_y + y,
15725                       GDI_WIDTH, gfx->width,
15726                       GDI_HEIGHT, gfx->height,
15727                       GDI_TYPE, button_type,
15728                       GDI_STATE, GD_BUTTON_UNPRESSED,
15729                       GDI_CHECKED, checked,
15730                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
15731                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
15732                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
15733                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
15734                       GDI_DIRECT_DRAW, FALSE,
15735                       GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
15736                       GDI_EVENT_MASK, event_mask,
15737                       GDI_CALLBACK_ACTION, HandleGameButtons,
15738                       GDI_END);
15739
15740     if (gi == NULL)
15741       Error(ERR_EXIT, "cannot create gadget");
15742
15743     game_gadget[id] = gi;
15744   }
15745 }
15746
15747 void FreeGameButtons(void)
15748 {
15749   int i;
15750
15751   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15752     FreeGadget(game_gadget[i]);
15753 }
15754
15755 static void UnmapGameButtonsAtSamePosition(int id)
15756 {
15757   int i;
15758
15759   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15760     if (i != id &&
15761         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15762         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15763       UnmapGadget(game_gadget[i]);
15764 }
15765
15766 static void UnmapGameButtonsAtSamePosition_All(void)
15767 {
15768   if (setup.show_snapshot_buttons)
15769   {
15770     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
15771     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
15772     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
15773   }
15774   else
15775   {
15776     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
15777     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
15778     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
15779
15780     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
15781     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
15782     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
15783   }
15784 }
15785
15786 static void MapGameButtonsAtSamePosition(int id)
15787 {
15788   int i;
15789
15790   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15791     if (i != id &&
15792         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15793         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15794       MapGadget(game_gadget[i]);
15795
15796   UnmapGameButtonsAtSamePosition_All();
15797 }
15798
15799 void MapUndoRedoButtons(void)
15800 {
15801   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15802   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15803
15804   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15805   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15806 }
15807
15808 void UnmapUndoRedoButtons(void)
15809 {
15810   UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15811   UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15812
15813   MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15814   MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15815 }
15816
15817 void ModifyPauseButtons(void)
15818 {
15819   static int ids[] =
15820   {
15821     GAME_CTRL_ID_PAUSE,
15822     GAME_CTRL_ID_PAUSE2,
15823     GAME_CTRL_ID_PANEL_PAUSE,
15824     GAME_CTRL_ID_TOUCH_PAUSE,
15825     -1
15826   };
15827   int i;
15828
15829   for (i = 0; ids[i] > -1; i++)
15830     ModifyGadget(game_gadget[ids[i]], GDI_CHECKED, tape.pausing, GDI_END);
15831 }
15832
15833 static void MapGameButtonsExt(boolean on_tape)
15834 {
15835   int i;
15836
15837   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15838     if ((!on_tape || gamebutton_info[i].allowed_on_tape) &&
15839         i != GAME_CTRL_ID_UNDO &&
15840         i != GAME_CTRL_ID_REDO)
15841       MapGadget(game_gadget[i]);
15842
15843   UnmapGameButtonsAtSamePosition_All();
15844
15845   RedrawGameButtons();
15846 }
15847
15848 static void UnmapGameButtonsExt(boolean on_tape)
15849 {
15850   int i;
15851
15852   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15853     if (!on_tape || gamebutton_info[i].allowed_on_tape)
15854       UnmapGadget(game_gadget[i]);
15855 }
15856
15857 static void RedrawGameButtonsExt(boolean on_tape)
15858 {
15859   int i;
15860
15861   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15862     if (!on_tape || gamebutton_info[i].allowed_on_tape)
15863       RedrawGadget(game_gadget[i]);
15864 }
15865
15866 static void SetGadgetState(struct GadgetInfo *gi, boolean state)
15867 {
15868   if (gi == NULL)
15869     return;
15870
15871   gi->checked = state;
15872 }
15873
15874 static void RedrawSoundButtonGadget(int id)
15875 {
15876   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
15877              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
15878              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
15879              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
15880              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
15881              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
15882              id);
15883
15884   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
15885   RedrawGadget(game_gadget[id2]);
15886 }
15887
15888 void MapGameButtons(void)
15889 {
15890   MapGameButtonsExt(FALSE);
15891 }
15892
15893 void UnmapGameButtons(void)
15894 {
15895   UnmapGameButtonsExt(FALSE);
15896 }
15897
15898 void RedrawGameButtons(void)
15899 {
15900   RedrawGameButtonsExt(FALSE);
15901 }
15902
15903 void MapGameButtonsOnTape(void)
15904 {
15905   MapGameButtonsExt(TRUE);
15906 }
15907
15908 void UnmapGameButtonsOnTape(void)
15909 {
15910   UnmapGameButtonsExt(TRUE);
15911 }
15912
15913 void RedrawGameButtonsOnTape(void)
15914 {
15915   RedrawGameButtonsExt(TRUE);
15916 }
15917
15918 static void GameUndoRedoExt(void)
15919 {
15920   ClearPlayerAction();
15921
15922   tape.pausing = TRUE;
15923
15924   RedrawPlayfield();
15925   UpdateAndDisplayGameControlValues();
15926
15927   DrawCompleteVideoDisplay();
15928   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
15929   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
15930   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
15931
15932   BackToFront();
15933 }
15934
15935 static void GameUndo(int steps)
15936 {
15937   if (!CheckEngineSnapshotList())
15938     return;
15939
15940   LoadEngineSnapshot_Undo(steps);
15941
15942   GameUndoRedoExt();
15943 }
15944
15945 static void GameRedo(int steps)
15946 {
15947   if (!CheckEngineSnapshotList())
15948     return;
15949
15950   LoadEngineSnapshot_Redo(steps);
15951
15952   GameUndoRedoExt();
15953 }
15954
15955 static void HandleGameButtonsExt(int id, int button)
15956 {
15957   static boolean game_undo_executed = FALSE;
15958   int steps = BUTTON_STEPSIZE(button);
15959   boolean handle_game_buttons =
15960     (game_status == GAME_MODE_PLAYING ||
15961      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
15962
15963   if (!handle_game_buttons)
15964     return;
15965
15966   switch (id)
15967   {
15968     case GAME_CTRL_ID_STOP:
15969     case GAME_CTRL_ID_PANEL_STOP:
15970     case GAME_CTRL_ID_TOUCH_STOP:
15971       if (game_status == GAME_MODE_MAIN)
15972         break;
15973
15974       if (tape.playing)
15975         TapeStop();
15976       else
15977         RequestQuitGame(TRUE);
15978
15979       break;
15980
15981     case GAME_CTRL_ID_PAUSE:
15982     case GAME_CTRL_ID_PAUSE2:
15983     case GAME_CTRL_ID_PANEL_PAUSE:
15984     case GAME_CTRL_ID_TOUCH_PAUSE:
15985       if (network.enabled && game_status == GAME_MODE_PLAYING)
15986       {
15987         if (tape.pausing)
15988           SendToServer_ContinuePlaying();
15989         else
15990           SendToServer_PausePlaying();
15991       }
15992       else
15993         TapeTogglePause(TAPE_TOGGLE_MANUAL);
15994
15995       game_undo_executed = FALSE;
15996
15997       break;
15998
15999     case GAME_CTRL_ID_PLAY:
16000     case GAME_CTRL_ID_PANEL_PLAY:
16001       if (game_status == GAME_MODE_MAIN)
16002       {
16003         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
16004       }
16005       else if (tape.pausing)
16006       {
16007         if (network.enabled)
16008           SendToServer_ContinuePlaying();
16009         else
16010           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
16011       }
16012       break;
16013
16014     case GAME_CTRL_ID_UNDO:
16015       // Important: When using "save snapshot when collecting an item" mode,
16016       // load last (current) snapshot for first "undo" after pressing "pause"
16017       // (else the last-but-one snapshot would be loaded, because the snapshot
16018       // pointer already points to the last snapshot when pressing "pause",
16019       // which is fine for "every step/move" mode, but not for "every collect")
16020       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16021           !game_undo_executed)
16022         steps--;
16023
16024       game_undo_executed = TRUE;
16025
16026       GameUndo(steps);
16027       break;
16028
16029     case GAME_CTRL_ID_REDO:
16030       GameRedo(steps);
16031       break;
16032
16033     case GAME_CTRL_ID_SAVE:
16034       TapeQuickSave();
16035       break;
16036
16037     case GAME_CTRL_ID_LOAD:
16038       TapeQuickLoad();
16039       break;
16040
16041     case SOUND_CTRL_ID_MUSIC:
16042     case SOUND_CTRL_ID_PANEL_MUSIC:
16043       if (setup.sound_music)
16044       { 
16045         setup.sound_music = FALSE;
16046
16047         FadeMusic();
16048       }
16049       else if (audio.music_available)
16050       { 
16051         setup.sound = setup.sound_music = TRUE;
16052
16053         SetAudioMode(setup.sound);
16054
16055         if (game_status == GAME_MODE_PLAYING)
16056           PlayLevelMusic();
16057       }
16058
16059       RedrawSoundButtonGadget(id);
16060
16061       break;
16062
16063     case SOUND_CTRL_ID_LOOPS:
16064     case SOUND_CTRL_ID_PANEL_LOOPS:
16065       if (setup.sound_loops)
16066         setup.sound_loops = FALSE;
16067       else if (audio.loops_available)
16068       {
16069         setup.sound = setup.sound_loops = TRUE;
16070
16071         SetAudioMode(setup.sound);
16072       }
16073
16074       RedrawSoundButtonGadget(id);
16075
16076       break;
16077
16078     case SOUND_CTRL_ID_SIMPLE:
16079     case SOUND_CTRL_ID_PANEL_SIMPLE:
16080       if (setup.sound_simple)
16081         setup.sound_simple = FALSE;
16082       else if (audio.sound_available)
16083       {
16084         setup.sound = setup.sound_simple = TRUE;
16085
16086         SetAudioMode(setup.sound);
16087       }
16088
16089       RedrawSoundButtonGadget(id);
16090
16091       break;
16092
16093     default:
16094       break;
16095   }
16096 }
16097
16098 static void HandleGameButtons(struct GadgetInfo *gi)
16099 {
16100   HandleGameButtonsExt(gi->custom_id, gi->event.button);
16101 }
16102
16103 void HandleSoundButtonKeys(Key key)
16104 {
16105   if (key == setup.shortcut.sound_simple)
16106     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
16107   else if (key == setup.shortcut.sound_loops)
16108     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
16109   else if (key == setup.shortcut.sound_music)
16110     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
16111 }