moved (and renamed) global variables to game structure (ZX/ZY)
[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 SOUND_CTRL_ID_MUSIC             11
1020 #define SOUND_CTRL_ID_LOOPS             12
1021 #define SOUND_CTRL_ID_SIMPLE            13
1022 #define SOUND_CTRL_ID_PANEL_MUSIC       14
1023 #define SOUND_CTRL_ID_PANEL_LOOPS       15
1024 #define SOUND_CTRL_ID_PANEL_SIMPLE      16
1025
1026 #define NUM_GAME_BUTTONS                17
1027
1028
1029 // forward declaration for internal use
1030
1031 static void CreateField(int, int, int);
1032
1033 static void ResetGfxAnimation(int, int);
1034
1035 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1036 static void AdvanceFrameAndPlayerCounters(int);
1037
1038 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1039 static boolean MovePlayer(struct PlayerInfo *, int, int);
1040 static void ScrollPlayer(struct PlayerInfo *, int);
1041 static void ScrollScreen(struct PlayerInfo *, int);
1042
1043 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1044 static boolean DigFieldByCE(int, int, int);
1045 static boolean SnapField(struct PlayerInfo *, int, int);
1046 static boolean DropElement(struct PlayerInfo *);
1047
1048 static void InitBeltMovement(void);
1049 static void CloseAllOpenTimegates(void);
1050 static void CheckGravityMovement(struct PlayerInfo *);
1051 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1052 static void KillPlayerUnlessEnemyProtected(int, int);
1053 static void KillPlayerUnlessExplosionProtected(int, int);
1054
1055 static void TestIfPlayerTouchesCustomElement(int, int);
1056 static void TestIfElementTouchesCustomElement(int, int);
1057 static void TestIfElementHitsCustomElement(int, int, int);
1058
1059 static void HandleElementChange(int, int, int);
1060 static void ExecuteCustomElementAction(int, int, int, int);
1061 static boolean ChangeElement(int, int, int, int);
1062
1063 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1064 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1065         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1066 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1067         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1068 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1069         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1070 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1071         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1072
1073 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1074 #define CheckElementChange(x, y, e, te, ev)                             \
1075         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1076 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1077         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1078 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1079         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1080
1081 static void PlayLevelSound(int, int, int);
1082 static void PlayLevelSoundNearest(int, int, int);
1083 static void PlayLevelSoundAction(int, int, int);
1084 static void PlayLevelSoundElementAction(int, int, int, int);
1085 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1086 static void PlayLevelSoundActionIfLoop(int, int, int);
1087 static void StopLevelSoundActionIfLoop(int, int, int);
1088 static void PlayLevelMusic(void);
1089 static void FadeLevelSoundsAndMusic(void);
1090
1091 static void HandleGameButtons(struct GadgetInfo *);
1092
1093 int AmoebeNachbarNr(int, int);
1094 void AmoebeUmwandeln(int, int);
1095 void ContinueMoving(int, int);
1096 void Bang(int, int);
1097 void InitMovDir(int, int);
1098 void InitAmoebaNr(int, int);
1099 int NewHiScore(int);
1100
1101 void TestIfGoodThingHitsBadThing(int, int, int);
1102 void TestIfBadThingHitsGoodThing(int, int, int);
1103 void TestIfPlayerTouchesBadThing(int, int);
1104 void TestIfPlayerRunsIntoBadThing(int, int, int);
1105 void TestIfBadThingTouchesPlayer(int, int);
1106 void TestIfBadThingRunsIntoPlayer(int, int, int);
1107 void TestIfFriendTouchesBadThing(int, int);
1108 void TestIfBadThingTouchesFriend(int, int);
1109 void TestIfBadThingTouchesOtherBadThing(int, int);
1110 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1111
1112 void KillPlayer(struct PlayerInfo *);
1113 void BuryPlayer(struct PlayerInfo *);
1114 void RemovePlayer(struct PlayerInfo *);
1115 void ExitPlayer(struct PlayerInfo *);
1116
1117 static int getInvisibleActiveFromInvisibleElement(int);
1118 static int getInvisibleFromInvisibleActiveElement(int);
1119
1120 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1121
1122 // for detection of endless loops, caused by custom element programming
1123 // (using maximal playfield width x 10 is just a rough approximation)
1124 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1125
1126 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1127 {                                                                       \
1128   if (recursion_loop_detected)                                          \
1129     return (rc);                                                        \
1130                                                                         \
1131   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1132   {                                                                     \
1133     recursion_loop_detected = TRUE;                                     \
1134     recursion_loop_element = (e);                                       \
1135   }                                                                     \
1136                                                                         \
1137   recursion_loop_depth++;                                               \
1138 }
1139
1140 #define RECURSION_LOOP_DETECTION_END()                                  \
1141 {                                                                       \
1142   recursion_loop_depth--;                                               \
1143 }
1144
1145 static int recursion_loop_depth;
1146 static boolean recursion_loop_detected;
1147 static boolean recursion_loop_element;
1148
1149 static int map_player_action[MAX_PLAYERS];
1150
1151
1152 // ----------------------------------------------------------------------------
1153 // definition of elements that automatically change to other elements after
1154 // a specified time, eventually calling a function when changing
1155 // ----------------------------------------------------------------------------
1156
1157 // forward declaration for changer functions
1158 static void InitBuggyBase(int, int);
1159 static void WarnBuggyBase(int, int);
1160
1161 static void InitTrap(int, int);
1162 static void ActivateTrap(int, int);
1163 static void ChangeActiveTrap(int, int);
1164
1165 static void InitRobotWheel(int, int);
1166 static void RunRobotWheel(int, int);
1167 static void StopRobotWheel(int, int);
1168
1169 static void InitTimegateWheel(int, int);
1170 static void RunTimegateWheel(int, int);
1171
1172 static void InitMagicBallDelay(int, int);
1173 static void ActivateMagicBall(int, int);
1174
1175 struct ChangingElementInfo
1176 {
1177   int element;
1178   int target_element;
1179   int change_delay;
1180   void (*pre_change_function)(int x, int y);
1181   void (*change_function)(int x, int y);
1182   void (*post_change_function)(int x, int y);
1183 };
1184
1185 static struct ChangingElementInfo change_delay_list[] =
1186 {
1187   {
1188     EL_NUT_BREAKING,
1189     EL_EMERALD,
1190     6,
1191     NULL,
1192     NULL,
1193     NULL
1194   },
1195   {
1196     EL_PEARL_BREAKING,
1197     EL_EMPTY,
1198     8,
1199     NULL,
1200     NULL,
1201     NULL
1202   },
1203   {
1204     EL_EXIT_OPENING,
1205     EL_EXIT_OPEN,
1206     29,
1207     NULL,
1208     NULL,
1209     NULL
1210   },
1211   {
1212     EL_EXIT_CLOSING,
1213     EL_EXIT_CLOSED,
1214     29,
1215     NULL,
1216     NULL,
1217     NULL
1218   },
1219   {
1220     EL_STEEL_EXIT_OPENING,
1221     EL_STEEL_EXIT_OPEN,
1222     29,
1223     NULL,
1224     NULL,
1225     NULL
1226   },
1227   {
1228     EL_STEEL_EXIT_CLOSING,
1229     EL_STEEL_EXIT_CLOSED,
1230     29,
1231     NULL,
1232     NULL,
1233     NULL
1234   },
1235   {
1236     EL_EM_EXIT_OPENING,
1237     EL_EM_EXIT_OPEN,
1238     29,
1239     NULL,
1240     NULL,
1241     NULL
1242   },
1243   {
1244     EL_EM_EXIT_CLOSING,
1245     EL_EMPTY,
1246     29,
1247     NULL,
1248     NULL,
1249     NULL
1250   },
1251   {
1252     EL_EM_STEEL_EXIT_OPENING,
1253     EL_EM_STEEL_EXIT_OPEN,
1254     29,
1255     NULL,
1256     NULL,
1257     NULL
1258   },
1259   {
1260     EL_EM_STEEL_EXIT_CLOSING,
1261     EL_STEELWALL,
1262     29,
1263     NULL,
1264     NULL,
1265     NULL
1266   },
1267   {
1268     EL_SP_EXIT_OPENING,
1269     EL_SP_EXIT_OPEN,
1270     29,
1271     NULL,
1272     NULL,
1273     NULL
1274   },
1275   {
1276     EL_SP_EXIT_CLOSING,
1277     EL_SP_EXIT_CLOSED,
1278     29,
1279     NULL,
1280     NULL,
1281     NULL
1282   },
1283   {
1284     EL_SWITCHGATE_OPENING,
1285     EL_SWITCHGATE_OPEN,
1286     29,
1287     NULL,
1288     NULL,
1289     NULL
1290   },
1291   {
1292     EL_SWITCHGATE_CLOSING,
1293     EL_SWITCHGATE_CLOSED,
1294     29,
1295     NULL,
1296     NULL,
1297     NULL
1298   },
1299   {
1300     EL_TIMEGATE_OPENING,
1301     EL_TIMEGATE_OPEN,
1302     29,
1303     NULL,
1304     NULL,
1305     NULL
1306   },
1307   {
1308     EL_TIMEGATE_CLOSING,
1309     EL_TIMEGATE_CLOSED,
1310     29,
1311     NULL,
1312     NULL,
1313     NULL
1314   },
1315
1316   {
1317     EL_ACID_SPLASH_LEFT,
1318     EL_EMPTY,
1319     8,
1320     NULL,
1321     NULL,
1322     NULL
1323   },
1324   {
1325     EL_ACID_SPLASH_RIGHT,
1326     EL_EMPTY,
1327     8,
1328     NULL,
1329     NULL,
1330     NULL
1331   },
1332   {
1333     EL_SP_BUGGY_BASE,
1334     EL_SP_BUGGY_BASE_ACTIVATING,
1335     0,
1336     InitBuggyBase,
1337     NULL,
1338     NULL
1339   },
1340   {
1341     EL_SP_BUGGY_BASE_ACTIVATING,
1342     EL_SP_BUGGY_BASE_ACTIVE,
1343     0,
1344     InitBuggyBase,
1345     NULL,
1346     NULL
1347   },
1348   {
1349     EL_SP_BUGGY_BASE_ACTIVE,
1350     EL_SP_BUGGY_BASE,
1351     0,
1352     InitBuggyBase,
1353     WarnBuggyBase,
1354     NULL
1355   },
1356   {
1357     EL_TRAP,
1358     EL_TRAP_ACTIVE,
1359     0,
1360     InitTrap,
1361     NULL,
1362     ActivateTrap
1363   },
1364   {
1365     EL_TRAP_ACTIVE,
1366     EL_TRAP,
1367     31,
1368     NULL,
1369     ChangeActiveTrap,
1370     NULL
1371   },
1372   {
1373     EL_ROBOT_WHEEL_ACTIVE,
1374     EL_ROBOT_WHEEL,
1375     0,
1376     InitRobotWheel,
1377     RunRobotWheel,
1378     StopRobotWheel
1379   },
1380   {
1381     EL_TIMEGATE_SWITCH_ACTIVE,
1382     EL_TIMEGATE_SWITCH,
1383     0,
1384     InitTimegateWheel,
1385     RunTimegateWheel,
1386     NULL
1387   },
1388   {
1389     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1390     EL_DC_TIMEGATE_SWITCH,
1391     0,
1392     InitTimegateWheel,
1393     RunTimegateWheel,
1394     NULL
1395   },
1396   {
1397     EL_EMC_MAGIC_BALL_ACTIVE,
1398     EL_EMC_MAGIC_BALL_ACTIVE,
1399     0,
1400     InitMagicBallDelay,
1401     NULL,
1402     ActivateMagicBall
1403   },
1404   {
1405     EL_EMC_SPRING_BUMPER_ACTIVE,
1406     EL_EMC_SPRING_BUMPER,
1407     8,
1408     NULL,
1409     NULL,
1410     NULL
1411   },
1412   {
1413     EL_DIAGONAL_SHRINKING,
1414     EL_UNDEFINED,
1415     0,
1416     NULL,
1417     NULL,
1418     NULL
1419   },
1420   {
1421     EL_DIAGONAL_GROWING,
1422     EL_UNDEFINED,
1423     0,
1424     NULL,
1425     NULL,
1426     NULL,
1427   },
1428
1429   {
1430     EL_UNDEFINED,
1431     EL_UNDEFINED,
1432     -1,
1433     NULL,
1434     NULL,
1435     NULL
1436   }
1437 };
1438
1439 struct
1440 {
1441   int element;
1442   int push_delay_fixed, push_delay_random;
1443 }
1444 push_delay_list[] =
1445 {
1446   { EL_SPRING,                  0, 0 },
1447   { EL_BALLOON,                 0, 0 },
1448
1449   { EL_SOKOBAN_OBJECT,          2, 0 },
1450   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1451   { EL_SATELLITE,               2, 0 },
1452   { EL_SP_DISK_YELLOW,          2, 0 },
1453
1454   { EL_UNDEFINED,               0, 0 },
1455 };
1456
1457 struct
1458 {
1459   int element;
1460   int move_stepsize;
1461 }
1462 move_stepsize_list[] =
1463 {
1464   { EL_AMOEBA_DROP,             2 },
1465   { EL_AMOEBA_DROPPING,         2 },
1466   { EL_QUICKSAND_FILLING,       1 },
1467   { EL_QUICKSAND_EMPTYING,      1 },
1468   { EL_QUICKSAND_FAST_FILLING,  2 },
1469   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1470   { EL_MAGIC_WALL_FILLING,      2 },
1471   { EL_MAGIC_WALL_EMPTYING,     2 },
1472   { EL_BD_MAGIC_WALL_FILLING,   2 },
1473   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1474   { EL_DC_MAGIC_WALL_FILLING,   2 },
1475   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1476
1477   { EL_UNDEFINED,               0 },
1478 };
1479
1480 struct
1481 {
1482   int element;
1483   int count;
1484 }
1485 collect_count_list[] =
1486 {
1487   { EL_EMERALD,                 1 },
1488   { EL_BD_DIAMOND,              1 },
1489   { EL_EMERALD_YELLOW,          1 },
1490   { EL_EMERALD_RED,             1 },
1491   { EL_EMERALD_PURPLE,          1 },
1492   { EL_DIAMOND,                 3 },
1493   { EL_SP_INFOTRON,             1 },
1494   { EL_PEARL,                   5 },
1495   { EL_CRYSTAL,                 8 },
1496
1497   { EL_UNDEFINED,               0 },
1498 };
1499
1500 struct
1501 {
1502   int element;
1503   int direction;
1504 }
1505 access_direction_list[] =
1506 {
1507   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1508   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1509   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1510   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1511   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1512   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1513   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1514   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1515   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1516   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1517   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1518
1519   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1520   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1521   { EL_SP_PORT_UP,                                                   MV_DOWN },
1522   { EL_SP_PORT_DOWN,                                         MV_UP           },
1523   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1524   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1525   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1526   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1527   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1528   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1529   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1530   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1531   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1532   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1533   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1534   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1535   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1536   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1537   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1538
1539   { EL_UNDEFINED,                       MV_NONE                              }
1540 };
1541
1542 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1543
1544 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1545 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1546 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1547                                  IS_JUST_CHANGING(x, y))
1548
1549 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1550
1551 // static variables for playfield scan mode (scanning forward or backward)
1552 static int playfield_scan_start_x = 0;
1553 static int playfield_scan_start_y = 0;
1554 static int playfield_scan_delta_x = 1;
1555 static int playfield_scan_delta_y = 1;
1556
1557 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1558                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1559                                      (y) += playfield_scan_delta_y)     \
1560                                 for ((x) = playfield_scan_start_x;      \
1561                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1562                                      (x) += playfield_scan_delta_x)
1563
1564 #ifdef DEBUG
1565 void DEBUG_SetMaximumDynamite(void)
1566 {
1567   int i;
1568
1569   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1570     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1571       local_player->inventory_element[local_player->inventory_size++] =
1572         EL_DYNAMITE;
1573 }
1574 #endif
1575
1576 static void InitPlayfieldScanModeVars(void)
1577 {
1578   if (game.use_reverse_scan_direction)
1579   {
1580     playfield_scan_start_x = lev_fieldx - 1;
1581     playfield_scan_start_y = lev_fieldy - 1;
1582
1583     playfield_scan_delta_x = -1;
1584     playfield_scan_delta_y = -1;
1585   }
1586   else
1587   {
1588     playfield_scan_start_x = 0;
1589     playfield_scan_start_y = 0;
1590
1591     playfield_scan_delta_x = 1;
1592     playfield_scan_delta_y = 1;
1593   }
1594 }
1595
1596 static void InitPlayfieldScanMode(int mode)
1597 {
1598   game.use_reverse_scan_direction =
1599     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1600
1601   InitPlayfieldScanModeVars();
1602 }
1603
1604 static int get_move_delay_from_stepsize(int move_stepsize)
1605 {
1606   move_stepsize =
1607     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1608
1609   // make sure that stepsize value is always a power of 2
1610   move_stepsize = (1 << log_2(move_stepsize));
1611
1612   return TILEX / move_stepsize;
1613 }
1614
1615 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1616                                boolean init_game)
1617 {
1618   int player_nr = player->index_nr;
1619   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1620   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1621
1622   // do no immediately change move delay -- the player might just be moving
1623   player->move_delay_value_next = move_delay;
1624
1625   // information if player can move must be set separately
1626   player->cannot_move = cannot_move;
1627
1628   if (init_game)
1629   {
1630     player->move_delay       = game.initial_move_delay[player_nr];
1631     player->move_delay_value = game.initial_move_delay_value[player_nr];
1632
1633     player->move_delay_value_next = -1;
1634
1635     player->move_delay_reset_counter = 0;
1636   }
1637 }
1638
1639 void GetPlayerConfig(void)
1640 {
1641   GameFrameDelay = setup.game_frame_delay;
1642
1643   if (!audio.sound_available)
1644     setup.sound_simple = FALSE;
1645
1646   if (!audio.loops_available)
1647     setup.sound_loops = FALSE;
1648
1649   if (!audio.music_available)
1650     setup.sound_music = FALSE;
1651
1652   if (!video.fullscreen_available)
1653     setup.fullscreen = FALSE;
1654
1655   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1656
1657   SetAudioMode(setup.sound);
1658 }
1659
1660 int GetElementFromGroupElement(int element)
1661 {
1662   if (IS_GROUP_ELEMENT(element))
1663   {
1664     struct ElementGroupInfo *group = element_info[element].group;
1665     int last_anim_random_frame = gfx.anim_random_frame;
1666     int element_pos;
1667
1668     if (group->choice_mode == ANIM_RANDOM)
1669       gfx.anim_random_frame = RND(group->num_elements_resolved);
1670
1671     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1672                                     group->choice_mode, 0,
1673                                     group->choice_pos);
1674
1675     if (group->choice_mode == ANIM_RANDOM)
1676       gfx.anim_random_frame = last_anim_random_frame;
1677
1678     group->choice_pos++;
1679
1680     element = group->element_resolved[element_pos];
1681   }
1682
1683   return element;
1684 }
1685
1686 static void IncrementSokobanFieldsNeeded(void)
1687 {
1688   if (level.sb_fields_needed)
1689     game.sokoban_fields_still_needed++;
1690 }
1691
1692 static void IncrementSokobanObjectsNeeded(void)
1693 {
1694   if (level.sb_objects_needed)
1695     game.sokoban_objects_still_needed++;
1696 }
1697
1698 static void DecrementSokobanFieldsNeeded(void)
1699 {
1700   if (game.sokoban_fields_still_needed > 0)
1701     game.sokoban_fields_still_needed--;
1702 }
1703
1704 static void DecrementSokobanObjectsNeeded(void)
1705 {
1706   if (game.sokoban_objects_still_needed > 0)
1707     game.sokoban_objects_still_needed--;
1708 }
1709
1710 static void InitPlayerField(int x, int y, int element, boolean init_game)
1711 {
1712   if (element == EL_SP_MURPHY)
1713   {
1714     if (init_game)
1715     {
1716       if (stored_player[0].present)
1717       {
1718         Feld[x][y] = EL_SP_MURPHY_CLONE;
1719
1720         return;
1721       }
1722       else
1723       {
1724         stored_player[0].initial_element = element;
1725         stored_player[0].use_murphy = TRUE;
1726
1727         if (!level.use_artwork_element[0])
1728           stored_player[0].artwork_element = EL_SP_MURPHY;
1729       }
1730
1731       Feld[x][y] = EL_PLAYER_1;
1732     }
1733   }
1734
1735   if (init_game)
1736   {
1737     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1738     int jx = player->jx, jy = player->jy;
1739
1740     player->present = TRUE;
1741
1742     player->block_last_field = (element == EL_SP_MURPHY ?
1743                                 level.sp_block_last_field :
1744                                 level.block_last_field);
1745
1746     // ---------- initialize player's last field block delay ------------------
1747
1748     // always start with reliable default value (no adjustment needed)
1749     player->block_delay_adjustment = 0;
1750
1751     // special case 1: in Supaplex, Murphy blocks last field one more frame
1752     if (player->block_last_field && element == EL_SP_MURPHY)
1753       player->block_delay_adjustment = 1;
1754
1755     // special case 2: in game engines before 3.1.1, blocking was different
1756     if (game.use_block_last_field_bug)
1757       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1758
1759     if (!network.enabled || player->connected_network)
1760     {
1761       player->active = TRUE;
1762
1763       // remove potentially duplicate players
1764       if (StorePlayer[jx][jy] == Feld[x][y])
1765         StorePlayer[jx][jy] = 0;
1766
1767       StorePlayer[x][y] = Feld[x][y];
1768
1769 #if DEBUG_INIT_PLAYER
1770       if (options.debug)
1771       {
1772         printf("- player element %d activated", player->element_nr);
1773         printf(" (local player is %d and currently %s)\n",
1774                local_player->element_nr,
1775                local_player->active ? "active" : "not active");
1776       }
1777     }
1778 #endif
1779
1780     Feld[x][y] = EL_EMPTY;
1781
1782     player->jx = player->last_jx = x;
1783     player->jy = player->last_jy = y;
1784   }
1785
1786   if (!init_game)
1787   {
1788     int player_nr = GET_PLAYER_NR(element);
1789     struct PlayerInfo *player = &stored_player[player_nr];
1790
1791     if (player->active && player->killed)
1792       player->reanimated = TRUE; // if player was just killed, reanimate him
1793   }
1794 }
1795
1796 static void InitField(int x, int y, boolean init_game)
1797 {
1798   int element = Feld[x][y];
1799
1800   switch (element)
1801   {
1802     case EL_SP_MURPHY:
1803     case EL_PLAYER_1:
1804     case EL_PLAYER_2:
1805     case EL_PLAYER_3:
1806     case EL_PLAYER_4:
1807       InitPlayerField(x, y, element, init_game);
1808       break;
1809
1810     case EL_SOKOBAN_FIELD_PLAYER:
1811       element = Feld[x][y] = EL_PLAYER_1;
1812       InitField(x, y, init_game);
1813
1814       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1815       InitField(x, y, init_game);
1816       break;
1817
1818     case EL_SOKOBAN_FIELD_EMPTY:
1819       IncrementSokobanFieldsNeeded();
1820       break;
1821
1822     case EL_SOKOBAN_OBJECT:
1823       IncrementSokobanObjectsNeeded();
1824       break;
1825
1826     case EL_STONEBLOCK:
1827       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1828         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1829       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1830         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1831       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1832         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1833       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1834         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1835       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1836         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1837       break;
1838
1839     case EL_BUG:
1840     case EL_BUG_RIGHT:
1841     case EL_BUG_UP:
1842     case EL_BUG_LEFT:
1843     case EL_BUG_DOWN:
1844     case EL_SPACESHIP:
1845     case EL_SPACESHIP_RIGHT:
1846     case EL_SPACESHIP_UP:
1847     case EL_SPACESHIP_LEFT:
1848     case EL_SPACESHIP_DOWN:
1849     case EL_BD_BUTTERFLY:
1850     case EL_BD_BUTTERFLY_RIGHT:
1851     case EL_BD_BUTTERFLY_UP:
1852     case EL_BD_BUTTERFLY_LEFT:
1853     case EL_BD_BUTTERFLY_DOWN:
1854     case EL_BD_FIREFLY:
1855     case EL_BD_FIREFLY_RIGHT:
1856     case EL_BD_FIREFLY_UP:
1857     case EL_BD_FIREFLY_LEFT:
1858     case EL_BD_FIREFLY_DOWN:
1859     case EL_PACMAN_RIGHT:
1860     case EL_PACMAN_UP:
1861     case EL_PACMAN_LEFT:
1862     case EL_PACMAN_DOWN:
1863     case EL_YAMYAM:
1864     case EL_YAMYAM_LEFT:
1865     case EL_YAMYAM_RIGHT:
1866     case EL_YAMYAM_UP:
1867     case EL_YAMYAM_DOWN:
1868     case EL_DARK_YAMYAM:
1869     case EL_ROBOT:
1870     case EL_PACMAN:
1871     case EL_SP_SNIKSNAK:
1872     case EL_SP_ELECTRON:
1873     case EL_MOLE:
1874     case EL_MOLE_LEFT:
1875     case EL_MOLE_RIGHT:
1876     case EL_MOLE_UP:
1877     case EL_MOLE_DOWN:
1878       InitMovDir(x, y);
1879       break;
1880
1881     case EL_AMOEBA_FULL:
1882     case EL_BD_AMOEBA:
1883       InitAmoebaNr(x, y);
1884       break;
1885
1886     case EL_AMOEBA_DROP:
1887       if (y == lev_fieldy - 1)
1888       {
1889         Feld[x][y] = EL_AMOEBA_GROWING;
1890         Store[x][y] = EL_AMOEBA_WET;
1891       }
1892       break;
1893
1894     case EL_DYNAMITE_ACTIVE:
1895     case EL_SP_DISK_RED_ACTIVE:
1896     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1897     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1898     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1899     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1900       MovDelay[x][y] = 96;
1901       break;
1902
1903     case EL_EM_DYNAMITE_ACTIVE:
1904       MovDelay[x][y] = 32;
1905       break;
1906
1907     case EL_LAMP:
1908       game.lights_still_needed++;
1909       break;
1910
1911     case EL_PENGUIN:
1912       game.friends_still_needed++;
1913       break;
1914
1915     case EL_PIG:
1916     case EL_DRAGON:
1917       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1918       break;
1919
1920     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1921     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1922     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1923     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1924     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1925     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1926     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1927     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1928     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1929     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1930     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1931     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1932       if (init_game)
1933       {
1934         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1935         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1936         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1937
1938         if (game.belt_dir_nr[belt_nr] == 3)     // initial value
1939         {
1940           game.belt_dir[belt_nr] = belt_dir;
1941           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1942         }
1943         else    // more than one switch -- set it like the first switch
1944         {
1945           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1946         }
1947       }
1948       break;
1949
1950     case EL_LIGHT_SWITCH_ACTIVE:
1951       if (init_game)
1952         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1953       break;
1954
1955     case EL_INVISIBLE_STEELWALL:
1956     case EL_INVISIBLE_WALL:
1957     case EL_INVISIBLE_SAND:
1958       if (game.light_time_left > 0 ||
1959           game.lenses_time_left > 0)
1960         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1961       break;
1962
1963     case EL_EMC_MAGIC_BALL:
1964       if (game.ball_state)
1965         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1966       break;
1967
1968     case EL_EMC_MAGIC_BALL_SWITCH:
1969       if (game.ball_state)
1970         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1971       break;
1972
1973     case EL_TRIGGER_PLAYER:
1974     case EL_TRIGGER_ELEMENT:
1975     case EL_TRIGGER_CE_VALUE:
1976     case EL_TRIGGER_CE_SCORE:
1977     case EL_SELF:
1978     case EL_ANY_ELEMENT:
1979     case EL_CURRENT_CE_VALUE:
1980     case EL_CURRENT_CE_SCORE:
1981     case EL_PREV_CE_1:
1982     case EL_PREV_CE_2:
1983     case EL_PREV_CE_3:
1984     case EL_PREV_CE_4:
1985     case EL_PREV_CE_5:
1986     case EL_PREV_CE_6:
1987     case EL_PREV_CE_7:
1988     case EL_PREV_CE_8:
1989     case EL_NEXT_CE_1:
1990     case EL_NEXT_CE_2:
1991     case EL_NEXT_CE_3:
1992     case EL_NEXT_CE_4:
1993     case EL_NEXT_CE_5:
1994     case EL_NEXT_CE_6:
1995     case EL_NEXT_CE_7:
1996     case EL_NEXT_CE_8:
1997       // reference elements should not be used on the playfield
1998       Feld[x][y] = EL_EMPTY;
1999       break;
2000
2001     default:
2002       if (IS_CUSTOM_ELEMENT(element))
2003       {
2004         if (CAN_MOVE(element))
2005           InitMovDir(x, y);
2006
2007         if (!element_info[element].use_last_ce_value || init_game)
2008           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
2009       }
2010       else if (IS_GROUP_ELEMENT(element))
2011       {
2012         Feld[x][y] = GetElementFromGroupElement(element);
2013
2014         InitField(x, y, init_game);
2015       }
2016
2017       break;
2018   }
2019
2020   if (!init_game)
2021     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2022 }
2023
2024 static void InitField_WithBug1(int x, int y, boolean init_game)
2025 {
2026   InitField(x, y, init_game);
2027
2028   // not needed to call InitMovDir() -- already done by InitField()!
2029   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2030       CAN_MOVE(Feld[x][y]))
2031     InitMovDir(x, y);
2032 }
2033
2034 static void InitField_WithBug2(int x, int y, boolean init_game)
2035 {
2036   int old_element = Feld[x][y];
2037
2038   InitField(x, y, init_game);
2039
2040   // not needed to call InitMovDir() -- already done by InitField()!
2041   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2042       CAN_MOVE(old_element) &&
2043       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2044     InitMovDir(x, y);
2045
2046   /* this case is in fact a combination of not less than three bugs:
2047      first, it calls InitMovDir() for elements that can move, although this is
2048      already done by InitField(); then, it checks the element that was at this
2049      field _before_ the call to InitField() (which can change it); lastly, it
2050      was not called for "mole with direction" elements, which were treated as
2051      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2052   */
2053 }
2054
2055 static int get_key_element_from_nr(int key_nr)
2056 {
2057   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2058                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2059                           EL_EM_KEY_1 : EL_KEY_1);
2060
2061   return key_base_element + key_nr;
2062 }
2063
2064 static int get_next_dropped_element(struct PlayerInfo *player)
2065 {
2066   return (player->inventory_size > 0 ?
2067           player->inventory_element[player->inventory_size - 1] :
2068           player->inventory_infinite_element != EL_UNDEFINED ?
2069           player->inventory_infinite_element :
2070           player->dynabombs_left > 0 ?
2071           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2072           EL_UNDEFINED);
2073 }
2074
2075 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2076 {
2077   // pos >= 0: get element from bottom of the stack;
2078   // pos <  0: get element from top of the stack
2079
2080   if (pos < 0)
2081   {
2082     int min_inventory_size = -pos;
2083     int inventory_pos = player->inventory_size - min_inventory_size;
2084     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2085
2086     return (player->inventory_size >= min_inventory_size ?
2087             player->inventory_element[inventory_pos] :
2088             player->inventory_infinite_element != EL_UNDEFINED ?
2089             player->inventory_infinite_element :
2090             player->dynabombs_left >= min_dynabombs_left ?
2091             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2092             EL_UNDEFINED);
2093   }
2094   else
2095   {
2096     int min_dynabombs_left = pos + 1;
2097     int min_inventory_size = pos + 1 - player->dynabombs_left;
2098     int inventory_pos = pos - player->dynabombs_left;
2099
2100     return (player->inventory_infinite_element != EL_UNDEFINED ?
2101             player->inventory_infinite_element :
2102             player->dynabombs_left >= min_dynabombs_left ?
2103             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2104             player->inventory_size >= min_inventory_size ?
2105             player->inventory_element[inventory_pos] :
2106             EL_UNDEFINED);
2107   }
2108 }
2109
2110 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2111 {
2112   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2113   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2114   int compare_result;
2115
2116   if (gpo1->sort_priority != gpo2->sort_priority)
2117     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2118   else
2119     compare_result = gpo1->nr - gpo2->nr;
2120
2121   return compare_result;
2122 }
2123
2124 int getPlayerInventorySize(int player_nr)
2125 {
2126   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2127     return level.native_em_level->ply[player_nr]->dynamite;
2128   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2129     return level.native_sp_level->game_sp->red_disk_count;
2130   else
2131     return stored_player[player_nr].inventory_size;
2132 }
2133
2134 static void InitGameControlValues(void)
2135 {
2136   int i;
2137
2138   for (i = 0; game_panel_controls[i].nr != -1; i++)
2139   {
2140     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2141     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2142     struct TextPosInfo *pos = gpc->pos;
2143     int nr = gpc->nr;
2144     int type = gpc->type;
2145
2146     if (nr != i)
2147     {
2148       Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2149       Error(ERR_EXIT, "this should not happen -- please debug");
2150     }
2151
2152     // force update of game controls after initialization
2153     gpc->value = gpc->last_value = -1;
2154     gpc->frame = gpc->last_frame = -1;
2155     gpc->gfx_frame = -1;
2156
2157     // determine panel value width for later calculation of alignment
2158     if (type == TYPE_INTEGER || type == TYPE_STRING)
2159     {
2160       pos->width = pos->size * getFontWidth(pos->font);
2161       pos->height = getFontHeight(pos->font);
2162     }
2163     else if (type == TYPE_ELEMENT)
2164     {
2165       pos->width = pos->size;
2166       pos->height = pos->size;
2167     }
2168
2169     // fill structure for game panel draw order
2170     gpo->nr = gpc->nr;
2171     gpo->sort_priority = pos->sort_priority;
2172   }
2173
2174   // sort game panel controls according to sort_priority and control number
2175   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2176         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2177 }
2178
2179 static void UpdatePlayfieldElementCount(void)
2180 {
2181   boolean use_element_count = FALSE;
2182   int i, j, x, y;
2183
2184   // first check if it is needed at all to calculate playfield element count
2185   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2186     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2187       use_element_count = TRUE;
2188
2189   if (!use_element_count)
2190     return;
2191
2192   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2193     element_info[i].element_count = 0;
2194
2195   SCAN_PLAYFIELD(x, y)
2196   {
2197     element_info[Feld[x][y]].element_count++;
2198   }
2199
2200   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2201     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2202       if (IS_IN_GROUP(j, i))
2203         element_info[EL_GROUP_START + i].element_count +=
2204           element_info[j].element_count;
2205 }
2206
2207 static void UpdateGameControlValues(void)
2208 {
2209   int i, k;
2210   int time = (game.LevelSolved ?
2211               game.LevelSolved_CountingTime :
2212               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2213               level.native_em_level->lev->time :
2214               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2215               level.native_sp_level->game_sp->time_played :
2216               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2217               game_mm.energy_left :
2218               game.no_time_limit ? TimePlayed : TimeLeft);
2219   int score = (game.LevelSolved ?
2220                game.LevelSolved_CountingScore :
2221                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2222                level.native_em_level->lev->score :
2223                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2224                level.native_sp_level->game_sp->score :
2225                level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2226                game_mm.score :
2227                game.score);
2228   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2229               level.native_em_level->lev->required :
2230               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2231               level.native_sp_level->game_sp->infotrons_still_needed :
2232               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2233               game_mm.kettles_still_needed :
2234               game.gems_still_needed);
2235   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2236                      level.native_em_level->lev->required > 0 :
2237                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2238                      level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2239                      level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2240                      game_mm.kettles_still_needed > 0 ||
2241                      game_mm.lights_still_needed > 0 :
2242                      game.gems_still_needed > 0 ||
2243                      game.sokoban_fields_still_needed > 0 ||
2244                      game.sokoban_objects_still_needed > 0 ||
2245                      game.lights_still_needed > 0);
2246   int health = (game.LevelSolved ?
2247                 game.LevelSolved_CountingHealth :
2248                 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2249                 MM_HEALTH(game_mm.laser_overload_value) :
2250                 game.health);
2251
2252   UpdatePlayfieldElementCount();
2253
2254   // update game panel control values
2255
2256   // used instead of "level_nr" (for network games)
2257   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = levelset.level_nr;
2258   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2259
2260   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2261   for (i = 0; i < MAX_NUM_KEYS; i++)
2262     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2263   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2264   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2265
2266   if (game.centered_player_nr == -1)
2267   {
2268     for (i = 0; i < MAX_PLAYERS; i++)
2269     {
2270       // only one player in Supaplex game engine
2271       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2272         break;
2273
2274       for (k = 0; k < MAX_NUM_KEYS; k++)
2275       {
2276         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2277         {
2278           if (level.native_em_level->ply[i]->keys & (1 << k))
2279             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2280               get_key_element_from_nr(k);
2281         }
2282         else if (stored_player[i].key[k])
2283           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2284             get_key_element_from_nr(k);
2285       }
2286
2287       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2288         getPlayerInventorySize(i);
2289
2290       if (stored_player[i].num_white_keys > 0)
2291         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2292           EL_DC_KEY_WHITE;
2293
2294       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2295         stored_player[i].num_white_keys;
2296     }
2297   }
2298   else
2299   {
2300     int player_nr = game.centered_player_nr;
2301
2302     for (k = 0; k < MAX_NUM_KEYS; k++)
2303     {
2304       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2305       {
2306         if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2307           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2308             get_key_element_from_nr(k);
2309       }
2310       else if (stored_player[player_nr].key[k])
2311         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2312           get_key_element_from_nr(k);
2313     }
2314
2315     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2316       getPlayerInventorySize(player_nr);
2317
2318     if (stored_player[player_nr].num_white_keys > 0)
2319       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2320
2321     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2322       stored_player[player_nr].num_white_keys;
2323   }
2324
2325   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2326   {
2327     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2328       get_inventory_element_from_pos(local_player, i);
2329     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2330       get_inventory_element_from_pos(local_player, -i - 1);
2331   }
2332
2333   game_panel_controls[GAME_PANEL_SCORE].value = score;
2334   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2335
2336   game_panel_controls[GAME_PANEL_TIME].value = time;
2337
2338   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2339   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2340   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2341
2342   if (level.time == 0)
2343     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2344   else
2345     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2346
2347   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2348   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2349
2350   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2351
2352   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2353     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2354      EL_EMPTY);
2355   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2356     local_player->shield_normal_time_left;
2357   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2358     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2359      EL_EMPTY);
2360   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2361     local_player->shield_deadly_time_left;
2362
2363   game_panel_controls[GAME_PANEL_EXIT].value =
2364     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2365
2366   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2367     (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2368   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2369     (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2370      EL_EMC_MAGIC_BALL_SWITCH);
2371
2372   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2373     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2374   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2375     game.light_time_left;
2376
2377   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2378     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2379   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2380     game.timegate_time_left;
2381
2382   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2383     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2384
2385   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2386     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2387   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2388     game.lenses_time_left;
2389
2390   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2391     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2392   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2393     game.magnify_time_left;
2394
2395   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2396     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2397      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2398      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2399      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2400      EL_BALLOON_SWITCH_NONE);
2401
2402   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2403     local_player->dynabomb_count;
2404   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2405     local_player->dynabomb_size;
2406   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2407     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2408
2409   game_panel_controls[GAME_PANEL_PENGUINS].value =
2410     game.friends_still_needed;
2411
2412   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2413     game.sokoban_objects_still_needed;
2414   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2415     game.sokoban_fields_still_needed;
2416
2417   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2418     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2419
2420   for (i = 0; i < NUM_BELTS; i++)
2421   {
2422     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2423       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2424        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2425     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2426       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2427   }
2428
2429   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2430     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2431   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2432     game.magic_wall_time_left;
2433
2434   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2435     local_player->gravity;
2436
2437   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2438     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2439
2440   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2441     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2442       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2443        game.panel.element[i].id : EL_UNDEFINED);
2444
2445   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2446     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2447       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2448        element_info[game.panel.element_count[i].id].element_count : 0);
2449
2450   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2451     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2452       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2453        element_info[game.panel.ce_score[i].id].collect_score : 0);
2454
2455   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2456     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2457       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2458        element_info[game.panel.ce_score_element[i].id].collect_score :
2459        EL_UNDEFINED);
2460
2461   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2462   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2463   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2464
2465   // update game panel control frames
2466
2467   for (i = 0; game_panel_controls[i].nr != -1; i++)
2468   {
2469     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2470
2471     if (gpc->type == TYPE_ELEMENT)
2472     {
2473       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2474       {
2475         int last_anim_random_frame = gfx.anim_random_frame;
2476         int element = gpc->value;
2477         int graphic = el2panelimg(element);
2478
2479         if (gpc->value != gpc->last_value)
2480         {
2481           gpc->gfx_frame = 0;
2482           gpc->gfx_random = INIT_GFX_RANDOM();
2483         }
2484         else
2485         {
2486           gpc->gfx_frame++;
2487
2488           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2489               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2490             gpc->gfx_random = INIT_GFX_RANDOM();
2491         }
2492
2493         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2494           gfx.anim_random_frame = gpc->gfx_random;
2495
2496         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2497           gpc->gfx_frame = element_info[element].collect_score;
2498
2499         gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2500                                               gpc->gfx_frame);
2501
2502         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2503           gfx.anim_random_frame = last_anim_random_frame;
2504       }
2505     }
2506     else if (gpc->type == TYPE_GRAPHIC)
2507     {
2508       if (gpc->graphic != IMG_UNDEFINED)
2509       {
2510         int last_anim_random_frame = gfx.anim_random_frame;
2511         int graphic = gpc->graphic;
2512
2513         if (gpc->value != gpc->last_value)
2514         {
2515           gpc->gfx_frame = 0;
2516           gpc->gfx_random = INIT_GFX_RANDOM();
2517         }
2518         else
2519         {
2520           gpc->gfx_frame++;
2521
2522           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2523               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2524             gpc->gfx_random = INIT_GFX_RANDOM();
2525         }
2526
2527         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2528           gfx.anim_random_frame = gpc->gfx_random;
2529
2530         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2531
2532         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2533           gfx.anim_random_frame = last_anim_random_frame;
2534       }
2535     }
2536   }
2537 }
2538
2539 static void DisplayGameControlValues(void)
2540 {
2541   boolean redraw_panel = FALSE;
2542   int i;
2543
2544   for (i = 0; game_panel_controls[i].nr != -1; i++)
2545   {
2546     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2547
2548     if (PANEL_DEACTIVATED(gpc->pos))
2549       continue;
2550
2551     if (gpc->value == gpc->last_value &&
2552         gpc->frame == gpc->last_frame)
2553       continue;
2554
2555     redraw_panel = TRUE;
2556   }
2557
2558   if (!redraw_panel)
2559     return;
2560
2561   // copy default game door content to main double buffer
2562
2563   // !!! CHECK AGAIN !!!
2564   SetPanelBackground();
2565   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2566   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2567
2568   // redraw game control buttons
2569   RedrawGameButtons();
2570
2571   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2572
2573   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2574   {
2575     int nr = game_panel_order[i].nr;
2576     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2577     struct TextPosInfo *pos = gpc->pos;
2578     int type = gpc->type;
2579     int value = gpc->value;
2580     int frame = gpc->frame;
2581     int size = pos->size;
2582     int font = pos->font;
2583     boolean draw_masked = pos->draw_masked;
2584     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2585
2586     if (PANEL_DEACTIVATED(pos))
2587       continue;
2588
2589     gpc->last_value = value;
2590     gpc->last_frame = frame;
2591
2592     if (type == TYPE_INTEGER)
2593     {
2594       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2595           nr == GAME_PANEL_TIME)
2596       {
2597         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2598
2599         if (use_dynamic_size)           // use dynamic number of digits
2600         {
2601           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2602           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2603           int size2 = size1 + 1;
2604           int font1 = pos->font;
2605           int font2 = pos->font_alt;
2606
2607           size = (value < value_change ? size1 : size2);
2608           font = (value < value_change ? font1 : font2);
2609         }
2610       }
2611
2612       // correct text size if "digits" is zero or less
2613       if (size <= 0)
2614         size = strlen(int2str(value, size));
2615
2616       // dynamically correct text alignment
2617       pos->width = size * getFontWidth(font);
2618
2619       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2620                   int2str(value, size), font, mask_mode);
2621     }
2622     else if (type == TYPE_ELEMENT)
2623     {
2624       int element, graphic;
2625       Bitmap *src_bitmap;
2626       int src_x, src_y;
2627       int width, height;
2628       int dst_x = PANEL_XPOS(pos);
2629       int dst_y = PANEL_YPOS(pos);
2630
2631       if (value != EL_UNDEFINED && value != EL_EMPTY)
2632       {
2633         element = value;
2634         graphic = el2panelimg(value);
2635
2636         // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2637
2638         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2639           size = TILESIZE;
2640
2641         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2642                               &src_x, &src_y);
2643
2644         width  = graphic_info[graphic].width  * size / TILESIZE;
2645         height = graphic_info[graphic].height * size / TILESIZE;
2646
2647         if (draw_masked)
2648           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2649                            dst_x, dst_y);
2650         else
2651           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2652                      dst_x, dst_y);
2653       }
2654     }
2655     else if (type == TYPE_GRAPHIC)
2656     {
2657       int graphic        = gpc->graphic;
2658       int graphic_active = gpc->graphic_active;
2659       Bitmap *src_bitmap;
2660       int src_x, src_y;
2661       int width, height;
2662       int dst_x = PANEL_XPOS(pos);
2663       int dst_y = PANEL_YPOS(pos);
2664       boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2665                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2666
2667       if (graphic != IMG_UNDEFINED && !skip)
2668       {
2669         if (pos->style == STYLE_REVERSE)
2670           value = 100 - value;
2671
2672         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2673
2674         if (pos->direction & MV_HORIZONTAL)
2675         {
2676           width  = graphic_info[graphic_active].width * value / 100;
2677           height = graphic_info[graphic_active].height;
2678
2679           if (pos->direction == MV_LEFT)
2680           {
2681             src_x += graphic_info[graphic_active].width - width;
2682             dst_x += graphic_info[graphic_active].width - width;
2683           }
2684         }
2685         else
2686         {
2687           width  = graphic_info[graphic_active].width;
2688           height = graphic_info[graphic_active].height * value / 100;
2689
2690           if (pos->direction == MV_UP)
2691           {
2692             src_y += graphic_info[graphic_active].height - height;
2693             dst_y += graphic_info[graphic_active].height - height;
2694           }
2695         }
2696
2697         if (draw_masked)
2698           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2699                            dst_x, dst_y);
2700         else
2701           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2702                      dst_x, dst_y);
2703
2704         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2705
2706         if (pos->direction & MV_HORIZONTAL)
2707         {
2708           if (pos->direction == MV_RIGHT)
2709           {
2710             src_x += width;
2711             dst_x += width;
2712           }
2713           else
2714           {
2715             dst_x = PANEL_XPOS(pos);
2716           }
2717
2718           width = graphic_info[graphic].width - width;
2719         }
2720         else
2721         {
2722           if (pos->direction == MV_DOWN)
2723           {
2724             src_y += height;
2725             dst_y += height;
2726           }
2727           else
2728           {
2729             dst_y = PANEL_YPOS(pos);
2730           }
2731
2732           height = graphic_info[graphic].height - height;
2733         }
2734
2735         if (draw_masked)
2736           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2737                            dst_x, dst_y);
2738         else
2739           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2740                      dst_x, dst_y);
2741       }
2742     }
2743     else if (type == TYPE_STRING)
2744     {
2745       boolean active = (value != 0);
2746       char *state_normal = "off";
2747       char *state_active = "on";
2748       char *state = (active ? state_active : state_normal);
2749       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2750                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2751                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2752                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2753
2754       if (nr == GAME_PANEL_GRAVITY_STATE)
2755       {
2756         int font1 = pos->font;          // (used for normal state)
2757         int font2 = pos->font_alt;      // (used for active state)
2758
2759         font = (active ? font2 : font1);
2760       }
2761
2762       if (s != NULL)
2763       {
2764         char *s_cut;
2765
2766         if (size <= 0)
2767         {
2768           // don't truncate output if "chars" is zero or less
2769           size = strlen(s);
2770
2771           // dynamically correct text alignment
2772           pos->width = size * getFontWidth(font);
2773         }
2774
2775         s_cut = getStringCopyN(s, size);
2776
2777         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2778                     s_cut, font, mask_mode);
2779
2780         free(s_cut);
2781       }
2782     }
2783
2784     redraw_mask |= REDRAW_DOOR_1;
2785   }
2786
2787   SetGameStatus(GAME_MODE_PLAYING);
2788 }
2789
2790 void UpdateAndDisplayGameControlValues(void)
2791 {
2792   if (tape.deactivate_display)
2793     return;
2794
2795   UpdateGameControlValues();
2796   DisplayGameControlValues();
2797 }
2798
2799 #if 0
2800 static void UpdateGameDoorValues(void)
2801 {
2802   UpdateGameControlValues();
2803 }
2804 #endif
2805
2806 void DrawGameDoorValues(void)
2807 {
2808   DisplayGameControlValues();
2809 }
2810
2811
2812 // ============================================================================
2813 // InitGameEngine()
2814 // ----------------------------------------------------------------------------
2815 // initialize game engine due to level / tape version number
2816 // ============================================================================
2817
2818 static void InitGameEngine(void)
2819 {
2820   int i, j, k, l, x, y;
2821
2822   // set game engine from tape file when re-playing, else from level file
2823   game.engine_version = (tape.playing ? tape.engine_version :
2824                          level.game_version);
2825
2826   // set single or multi-player game mode (needed for re-playing tapes)
2827   game.team_mode = setup.team_mode;
2828
2829   if (tape.playing)
2830   {
2831     int num_players = 0;
2832
2833     for (i = 0; i < MAX_PLAYERS; i++)
2834       if (tape.player_participates[i])
2835         num_players++;
2836
2837     // multi-player tapes contain input data for more than one player
2838     game.team_mode = (num_players > 1);
2839   }
2840
2841   // --------------------------------------------------------------------------
2842   // set flags for bugs and changes according to active game engine version
2843   // --------------------------------------------------------------------------
2844
2845   /*
2846     Summary of bugfix/change:
2847     Fixed handling for custom elements that change when pushed by the player.
2848
2849     Fixed/changed in version:
2850     3.1.0
2851
2852     Description:
2853     Before 3.1.0, custom elements that "change when pushing" changed directly
2854     after the player started pushing them (until then handled in "DigField()").
2855     Since 3.1.0, these custom elements are not changed until the "pushing"
2856     move of the element is finished (now handled in "ContinueMoving()").
2857
2858     Affected levels/tapes:
2859     The first condition is generally needed for all levels/tapes before version
2860     3.1.0, which might use the old behaviour before it was changed; known tapes
2861     that are affected are some tapes from the level set "Walpurgis Gardens" by
2862     Jamie Cullen.
2863     The second condition is an exception from the above case and is needed for
2864     the special case of tapes recorded with game (not engine!) version 3.1.0 or
2865     above (including some development versions of 3.1.0), but before it was
2866     known that this change would break tapes like the above and was fixed in
2867     3.1.1, so that the changed behaviour was active although the engine version
2868     while recording maybe was before 3.1.0. There is at least one tape that is
2869     affected by this exception, which is the tape for the one-level set "Bug
2870     Machine" by Juergen Bonhagen.
2871   */
2872
2873   game.use_change_when_pushing_bug =
2874     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2875      !(tape.playing &&
2876        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2877        tape.game_version <  VERSION_IDENT(3,1,1,0)));
2878
2879   /*
2880     Summary of bugfix/change:
2881     Fixed handling for blocking the field the player leaves when moving.
2882
2883     Fixed/changed in version:
2884     3.1.1
2885
2886     Description:
2887     Before 3.1.1, when "block last field when moving" was enabled, the field
2888     the player is leaving when moving was blocked for the time of the move,
2889     and was directly unblocked afterwards. This resulted in the last field
2890     being blocked for exactly one less than the number of frames of one player
2891     move. Additionally, even when blocking was disabled, the last field was
2892     blocked for exactly one frame.
2893     Since 3.1.1, due to changes in player movement handling, the last field
2894     is not blocked at all when blocking is disabled. When blocking is enabled,
2895     the last field is blocked for exactly the number of frames of one player
2896     move. Additionally, if the player is Murphy, the hero of Supaplex, the
2897     last field is blocked for exactly one more than the number of frames of
2898     one player move.
2899
2900     Affected levels/tapes:
2901     (!!! yet to be determined -- probably many !!!)
2902   */
2903
2904   game.use_block_last_field_bug =
2905     (game.engine_version < VERSION_IDENT(3,1,1,0));
2906
2907   game_em.use_single_button =
2908     (game.engine_version > VERSION_IDENT(4,0,0,2));
2909
2910   game_em.use_snap_key_bug =
2911     (game.engine_version < VERSION_IDENT(4,0,1,0));
2912
2913   // --------------------------------------------------------------------------
2914
2915   // set maximal allowed number of custom element changes per game frame
2916   game.max_num_changes_per_frame = 1;
2917
2918   // default scan direction: scan playfield from top/left to bottom/right
2919   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2920
2921   // dynamically adjust element properties according to game engine version
2922   InitElementPropertiesEngine(game.engine_version);
2923
2924 #if 0
2925   printf("level %d: level version == %06d\n", level_nr, level.game_version);
2926   printf("          tape version == %06d [%s] [file: %06d]\n",
2927          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2928          tape.file_version);
2929   printf("       => game.engine_version == %06d\n", game.engine_version);
2930 #endif
2931
2932   // ---------- initialize player's initial move delay ------------------------
2933
2934   // dynamically adjust player properties according to level information
2935   for (i = 0; i < MAX_PLAYERS; i++)
2936     game.initial_move_delay_value[i] =
2937       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2938
2939   // dynamically adjust player properties according to game engine version
2940   for (i = 0; i < MAX_PLAYERS; i++)
2941     game.initial_move_delay[i] =
2942       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2943        game.initial_move_delay_value[i] : 0);
2944
2945   // ---------- initialize player's initial push delay ------------------------
2946
2947   // dynamically adjust player properties according to game engine version
2948   game.initial_push_delay_value =
2949     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2950
2951   // ---------- initialize changing elements ----------------------------------
2952
2953   // initialize changing elements information
2954   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2955   {
2956     struct ElementInfo *ei = &element_info[i];
2957
2958     // this pointer might have been changed in the level editor
2959     ei->change = &ei->change_page[0];
2960
2961     if (!IS_CUSTOM_ELEMENT(i))
2962     {
2963       ei->change->target_element = EL_EMPTY_SPACE;
2964       ei->change->delay_fixed = 0;
2965       ei->change->delay_random = 0;
2966       ei->change->delay_frames = 1;
2967     }
2968
2969     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2970     {
2971       ei->has_change_event[j] = FALSE;
2972
2973       ei->event_page_nr[j] = 0;
2974       ei->event_page[j] = &ei->change_page[0];
2975     }
2976   }
2977
2978   // add changing elements from pre-defined list
2979   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2980   {
2981     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2982     struct ElementInfo *ei = &element_info[ch_delay->element];
2983
2984     ei->change->target_element       = ch_delay->target_element;
2985     ei->change->delay_fixed          = ch_delay->change_delay;
2986
2987     ei->change->pre_change_function  = ch_delay->pre_change_function;
2988     ei->change->change_function      = ch_delay->change_function;
2989     ei->change->post_change_function = ch_delay->post_change_function;
2990
2991     ei->change->can_change = TRUE;
2992     ei->change->can_change_or_has_action = TRUE;
2993
2994     ei->has_change_event[CE_DELAY] = TRUE;
2995
2996     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
2997     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
2998   }
2999
3000   // ---------- initialize internal run-time variables ------------------------
3001
3002   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3003   {
3004     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3005
3006     for (j = 0; j < ei->num_change_pages; j++)
3007     {
3008       ei->change_page[j].can_change_or_has_action =
3009         (ei->change_page[j].can_change |
3010          ei->change_page[j].has_action);
3011     }
3012   }
3013
3014   // add change events from custom element configuration
3015   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3016   {
3017     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3018
3019     for (j = 0; j < ei->num_change_pages; j++)
3020     {
3021       if (!ei->change_page[j].can_change_or_has_action)
3022         continue;
3023
3024       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3025       {
3026         // only add event page for the first page found with this event
3027         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3028         {
3029           ei->has_change_event[k] = TRUE;
3030
3031           ei->event_page_nr[k] = j;
3032           ei->event_page[k] = &ei->change_page[j];
3033         }
3034       }
3035     }
3036   }
3037
3038   // ---------- initialize reference elements in change conditions ------------
3039
3040   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3041   {
3042     int element = EL_CUSTOM_START + i;
3043     struct ElementInfo *ei = &element_info[element];
3044
3045     for (j = 0; j < ei->num_change_pages; j++)
3046     {
3047       int trigger_element = ei->change_page[j].initial_trigger_element;
3048
3049       if (trigger_element >= EL_PREV_CE_8 &&
3050           trigger_element <= EL_NEXT_CE_8)
3051         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3052
3053       ei->change_page[j].trigger_element = trigger_element;
3054     }
3055   }
3056
3057   // ---------- initialize run-time trigger player and element ----------------
3058
3059   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3060   {
3061     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3062
3063     for (j = 0; j < ei->num_change_pages; j++)
3064     {
3065       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3066       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3067       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3068       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3069       ei->change_page[j].actual_trigger_ce_value = 0;
3070       ei->change_page[j].actual_trigger_ce_score = 0;
3071     }
3072   }
3073
3074   // ---------- initialize trigger events -------------------------------------
3075
3076   // initialize trigger events information
3077   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3078     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3079       trigger_events[i][j] = FALSE;
3080
3081   // add trigger events from element change event properties
3082   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3083   {
3084     struct ElementInfo *ei = &element_info[i];
3085
3086     for (j = 0; j < ei->num_change_pages; j++)
3087     {
3088       if (!ei->change_page[j].can_change_or_has_action)
3089         continue;
3090
3091       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3092       {
3093         int trigger_element = ei->change_page[j].trigger_element;
3094
3095         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3096         {
3097           if (ei->change_page[j].has_event[k])
3098           {
3099             if (IS_GROUP_ELEMENT(trigger_element))
3100             {
3101               struct ElementGroupInfo *group =
3102                 element_info[trigger_element].group;
3103
3104               for (l = 0; l < group->num_elements_resolved; l++)
3105                 trigger_events[group->element_resolved[l]][k] = TRUE;
3106             }
3107             else if (trigger_element == EL_ANY_ELEMENT)
3108               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3109                 trigger_events[l][k] = TRUE;
3110             else
3111               trigger_events[trigger_element][k] = TRUE;
3112           }
3113         }
3114       }
3115     }
3116   }
3117
3118   // ---------- initialize push delay -----------------------------------------
3119
3120   // initialize push delay values to default
3121   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3122   {
3123     if (!IS_CUSTOM_ELEMENT(i))
3124     {
3125       // set default push delay values (corrected since version 3.0.7-1)
3126       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3127       {
3128         element_info[i].push_delay_fixed = 2;
3129         element_info[i].push_delay_random = 8;
3130       }
3131       else
3132       {
3133         element_info[i].push_delay_fixed = 8;
3134         element_info[i].push_delay_random = 8;
3135       }
3136     }
3137   }
3138
3139   // set push delay value for certain elements from pre-defined list
3140   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3141   {
3142     int e = push_delay_list[i].element;
3143
3144     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3145     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3146   }
3147
3148   // set push delay value for Supaplex elements for newer engine versions
3149   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3150   {
3151     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3152     {
3153       if (IS_SP_ELEMENT(i))
3154       {
3155         // set SP push delay to just enough to push under a falling zonk
3156         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3157
3158         element_info[i].push_delay_fixed  = delay;
3159         element_info[i].push_delay_random = 0;
3160       }
3161     }
3162   }
3163
3164   // ---------- initialize move stepsize --------------------------------------
3165
3166   // initialize move stepsize values to default
3167   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3168     if (!IS_CUSTOM_ELEMENT(i))
3169       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3170
3171   // set move stepsize value for certain elements from pre-defined list
3172   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3173   {
3174     int e = move_stepsize_list[i].element;
3175
3176     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3177   }
3178
3179   // ---------- initialize collect score --------------------------------------
3180
3181   // initialize collect score values for custom elements from initial value
3182   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3183     if (IS_CUSTOM_ELEMENT(i))
3184       element_info[i].collect_score = element_info[i].collect_score_initial;
3185
3186   // ---------- initialize collect count --------------------------------------
3187
3188   // initialize collect count values for non-custom elements
3189   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3190     if (!IS_CUSTOM_ELEMENT(i))
3191       element_info[i].collect_count_initial = 0;
3192
3193   // add collect count values for all elements from pre-defined list
3194   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3195     element_info[collect_count_list[i].element].collect_count_initial =
3196       collect_count_list[i].count;
3197
3198   // ---------- initialize access direction -----------------------------------
3199
3200   // initialize access direction values to default (access from every side)
3201   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3202     if (!IS_CUSTOM_ELEMENT(i))
3203       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3204
3205   // set access direction value for certain elements from pre-defined list
3206   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3207     element_info[access_direction_list[i].element].access_direction =
3208       access_direction_list[i].direction;
3209
3210   // ---------- initialize explosion content ----------------------------------
3211   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3212   {
3213     if (IS_CUSTOM_ELEMENT(i))
3214       continue;
3215
3216     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3217     {
3218       // (content for EL_YAMYAM set at run-time with game.yamyam_content_nr)
3219
3220       element_info[i].content.e[x][y] =
3221         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3222          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3223          i == EL_PLAYER_3 ? EL_EMERALD :
3224          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3225          i == EL_MOLE ? EL_EMERALD_RED :
3226          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3227          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3228          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3229          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3230          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3231          i == EL_WALL_EMERALD ? EL_EMERALD :
3232          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3233          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3234          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3235          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3236          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3237          i == EL_WALL_PEARL ? EL_PEARL :
3238          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3239          EL_EMPTY);
3240     }
3241   }
3242
3243   // ---------- initialize recursion detection --------------------------------
3244   recursion_loop_depth = 0;
3245   recursion_loop_detected = FALSE;
3246   recursion_loop_element = EL_UNDEFINED;
3247
3248   // ---------- initialize graphics engine ------------------------------------
3249   game.scroll_delay_value =
3250     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3251      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3252   game.scroll_delay_value =
3253     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3254
3255   // ---------- initialize game engine snapshots ------------------------------
3256   for (i = 0; i < MAX_PLAYERS; i++)
3257     game.snapshot.last_action[i] = 0;
3258   game.snapshot.changed_action = FALSE;
3259   game.snapshot.collected_item = FALSE;
3260   game.snapshot.mode =
3261     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3262      SNAPSHOT_MODE_EVERY_STEP :
3263      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3264      SNAPSHOT_MODE_EVERY_MOVE :
3265      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3266      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3267   game.snapshot.save_snapshot = FALSE;
3268
3269   // ---------- initialize level time for Supaplex engine ---------------------
3270   // Supaplex levels with time limit currently unsupported -- should be added
3271   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3272     level.time = 0;
3273 }
3274
3275 static int get_num_special_action(int element, int action_first,
3276                                   int action_last)
3277 {
3278   int num_special_action = 0;
3279   int i, j;
3280
3281   for (i = action_first; i <= action_last; i++)
3282   {
3283     boolean found = FALSE;
3284
3285     for (j = 0; j < NUM_DIRECTIONS; j++)
3286       if (el_act_dir2img(element, i, j) !=
3287           el_act_dir2img(element, ACTION_DEFAULT, j))
3288         found = TRUE;
3289
3290     if (found)
3291       num_special_action++;
3292     else
3293       break;
3294   }
3295
3296   return num_special_action;
3297 }
3298
3299
3300 // ============================================================================
3301 // InitGame()
3302 // ----------------------------------------------------------------------------
3303 // initialize and start new game
3304 // ============================================================================
3305
3306 #if DEBUG_INIT_PLAYER
3307 static void DebugPrintPlayerStatus(char *message)
3308 {
3309   int i;
3310
3311   if (!options.debug)
3312     return;
3313
3314   printf("%s:\n", message);
3315
3316   for (i = 0; i < MAX_PLAYERS; i++)
3317   {
3318     struct PlayerInfo *player = &stored_player[i];
3319
3320     printf("- player %d: present == %d, connected == %d [%d/%d], active == %d",
3321            i + 1,
3322            player->present,
3323            player->connected,
3324            player->connected_locally,
3325            player->connected_network,
3326            player->active);
3327
3328     if (local_player == player)
3329       printf(" (local player)");
3330
3331     printf("\n");
3332   }
3333 }
3334 #endif
3335
3336 void InitGame(void)
3337 {
3338   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3339   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3340   int fade_mask = REDRAW_FIELD;
3341
3342   boolean emulate_bd = TRUE;    // unless non-BOULDERDASH elements found
3343   boolean emulate_sb = TRUE;    // unless non-SOKOBAN     elements found
3344   boolean emulate_sp = TRUE;    // unless non-SUPAPLEX    elements found
3345   int initial_move_dir = MV_DOWN;
3346   int i, j, x, y;
3347
3348   // required here to update video display before fading (FIX THIS)
3349   DrawMaskedBorder(REDRAW_DOOR_2);
3350
3351   if (!game.restart_level)
3352     CloseDoor(DOOR_CLOSE_1);
3353
3354   SetGameStatus(GAME_MODE_PLAYING);
3355
3356   if (level_editor_test_game)
3357     FadeSkipNextFadeIn();
3358   else
3359     FadeSetEnterScreen();
3360
3361   if (CheckIfGlobalBorderOrPlayfieldViewportHasChanged())
3362     fade_mask = REDRAW_ALL;
3363
3364   FadeLevelSoundsAndMusic();
3365
3366   ExpireSoundLoops(TRUE);
3367
3368   FadeOut(fade_mask);
3369
3370   // needed if different viewport properties defined for playing
3371   ChangeViewportPropertiesIfNeeded();
3372
3373   ClearField();
3374
3375   DrawCompleteVideoDisplay();
3376
3377   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3378
3379   InitGameEngine();
3380   InitGameControlValues();
3381
3382   // don't play tapes over network
3383   network_playing = (network.enabled && !tape.playing);
3384
3385   for (i = 0; i < MAX_PLAYERS; i++)
3386   {
3387     struct PlayerInfo *player = &stored_player[i];
3388
3389     player->index_nr = i;
3390     player->index_bit = (1 << i);
3391     player->element_nr = EL_PLAYER_1 + i;
3392
3393     player->present = FALSE;
3394     player->active = FALSE;
3395     player->mapped = FALSE;
3396
3397     player->killed = FALSE;
3398     player->reanimated = FALSE;
3399     player->buried = FALSE;
3400
3401     player->action = 0;
3402     player->effective_action = 0;
3403     player->programmed_action = 0;
3404
3405     player->mouse_action.lx = 0;
3406     player->mouse_action.ly = 0;
3407     player->mouse_action.button = 0;
3408     player->mouse_action.button_hint = 0;
3409
3410     player->effective_mouse_action.lx = 0;
3411     player->effective_mouse_action.ly = 0;
3412     player->effective_mouse_action.button = 0;
3413     player->effective_mouse_action.button_hint = 0;
3414
3415     for (j = 0; j < MAX_NUM_KEYS; j++)
3416       player->key[j] = FALSE;
3417
3418     player->num_white_keys = 0;
3419
3420     player->dynabomb_count = 0;
3421     player->dynabomb_size = 1;
3422     player->dynabombs_left = 0;
3423     player->dynabomb_xl = FALSE;
3424
3425     player->MovDir = initial_move_dir;
3426     player->MovPos = 0;
3427     player->GfxPos = 0;
3428     player->GfxDir = initial_move_dir;
3429     player->GfxAction = ACTION_DEFAULT;
3430     player->Frame = 0;
3431     player->StepFrame = 0;
3432
3433     player->initial_element = player->element_nr;
3434     player->artwork_element =
3435       (level.use_artwork_element[i] ? level.artwork_element[i] :
3436        player->element_nr);
3437     player->use_murphy = FALSE;
3438
3439     player->block_last_field = FALSE;   // initialized in InitPlayerField()
3440     player->block_delay_adjustment = 0; // initialized in InitPlayerField()
3441
3442     player->gravity = level.initial_player_gravity[i];
3443
3444     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3445
3446     player->actual_frame_counter = 0;
3447
3448     player->step_counter = 0;
3449
3450     player->last_move_dir = initial_move_dir;
3451
3452     player->is_active = FALSE;
3453
3454     player->is_waiting = FALSE;
3455     player->is_moving = FALSE;
3456     player->is_auto_moving = FALSE;
3457     player->is_digging = FALSE;
3458     player->is_snapping = FALSE;
3459     player->is_collecting = FALSE;
3460     player->is_pushing = FALSE;
3461     player->is_switching = FALSE;
3462     player->is_dropping = FALSE;
3463     player->is_dropping_pressed = FALSE;
3464
3465     player->is_bored = FALSE;
3466     player->is_sleeping = FALSE;
3467
3468     player->was_waiting = TRUE;
3469     player->was_moving = FALSE;
3470     player->was_snapping = FALSE;
3471     player->was_dropping = FALSE;
3472
3473     player->force_dropping = FALSE;
3474
3475     player->frame_counter_bored = -1;
3476     player->frame_counter_sleeping = -1;
3477
3478     player->anim_delay_counter = 0;
3479     player->post_delay_counter = 0;
3480
3481     player->dir_waiting = initial_move_dir;
3482     player->action_waiting = ACTION_DEFAULT;
3483     player->last_action_waiting = ACTION_DEFAULT;
3484     player->special_action_bored = ACTION_DEFAULT;
3485     player->special_action_sleeping = ACTION_DEFAULT;
3486
3487     player->switch_x = -1;
3488     player->switch_y = -1;
3489
3490     player->drop_x = -1;
3491     player->drop_y = -1;
3492
3493     player->show_envelope = 0;
3494
3495     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3496
3497     player->push_delay       = -1;      // initialized when pushing starts
3498     player->push_delay_value = game.initial_push_delay_value;
3499
3500     player->drop_delay = 0;
3501     player->drop_pressed_delay = 0;
3502
3503     player->last_jx = -1;
3504     player->last_jy = -1;
3505     player->jx = -1;
3506     player->jy = -1;
3507
3508     player->shield_normal_time_left = 0;
3509     player->shield_deadly_time_left = 0;
3510
3511     player->inventory_infinite_element = EL_UNDEFINED;
3512     player->inventory_size = 0;
3513
3514     if (level.use_initial_inventory[i])
3515     {
3516       for (j = 0; j < level.initial_inventory_size[i]; j++)
3517       {
3518         int element = level.initial_inventory_content[i][j];
3519         int collect_count = element_info[element].collect_count_initial;
3520         int k;
3521
3522         if (!IS_CUSTOM_ELEMENT(element))
3523           collect_count = 1;
3524
3525         if (collect_count == 0)
3526           player->inventory_infinite_element = element;
3527         else
3528           for (k = 0; k < collect_count; k++)
3529             if (player->inventory_size < MAX_INVENTORY_SIZE)
3530               player->inventory_element[player->inventory_size++] = element;
3531       }
3532     }
3533
3534     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3535     SnapField(player, 0, 0);
3536
3537     map_player_action[i] = i;
3538   }
3539
3540   network_player_action_received = FALSE;
3541
3542   // initial null action
3543   if (network_playing)
3544     SendToServer_MovePlayer(MV_NONE);
3545
3546   FrameCounter = 0;
3547   TimeFrames = 0;
3548   TimePlayed = 0;
3549   TimeLeft = level.time;
3550   TapeTime = 0;
3551
3552   ScreenMovDir = MV_NONE;
3553   ScreenMovPos = 0;
3554   ScreenGfxPos = 0;
3555
3556   ScrollStepSize = 0;   // will be correctly initialized by ScrollScreen()
3557
3558   game.robot_wheel_x = -1;
3559   game.robot_wheel_y = -1;
3560
3561   game.exit_x = -1;
3562   game.exit_y = -1;
3563
3564   game.all_players_gone = FALSE;
3565
3566   game.LevelSolved = FALSE;
3567   game.GameOver = FALSE;
3568
3569   game.LevelSolved_GameWon = FALSE;
3570   game.LevelSolved_GameEnd = FALSE;
3571   game.LevelSolved_SaveTape = FALSE;
3572   game.LevelSolved_SaveScore = FALSE;
3573
3574   game.LevelSolved_CountingTime = 0;
3575   game.LevelSolved_CountingScore = 0;
3576   game.LevelSolved_CountingHealth = 0;
3577
3578   game.panel.active = TRUE;
3579
3580   game.no_time_limit = (level.time == 0);
3581
3582   game.yamyam_content_nr = 0;
3583   game.robot_wheel_active = FALSE;
3584   game.magic_wall_active = FALSE;
3585   game.magic_wall_time_left = 0;
3586   game.light_time_left = 0;
3587   game.timegate_time_left = 0;
3588   game.switchgate_pos = 0;
3589   game.wind_direction = level.wind_direction_initial;
3590
3591   game.score = 0;
3592   game.score_final = 0;
3593
3594   game.health = MAX_HEALTH;
3595   game.health_final = MAX_HEALTH;
3596
3597   game.gems_still_needed = level.gems_needed;
3598   game.sokoban_fields_still_needed = 0;
3599   game.sokoban_objects_still_needed = 0;
3600   game.lights_still_needed = 0;
3601   game.players_still_needed = 0;
3602   game.friends_still_needed = 0;
3603
3604   game.lenses_time_left = 0;
3605   game.magnify_time_left = 0;
3606
3607   game.ball_state = level.ball_state_initial;
3608   game.ball_content_nr = 0;
3609
3610   game.explosions_delayed = TRUE;
3611
3612   game.envelope_active = FALSE;
3613
3614   for (i = 0; i < NUM_BELTS; i++)
3615   {
3616     game.belt_dir[i] = MV_NONE;
3617     game.belt_dir_nr[i] = 3;            // not moving, next moving left
3618   }
3619
3620   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3621     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3622
3623 #if DEBUG_INIT_PLAYER
3624   DebugPrintPlayerStatus("Player status at level initialization");
3625 #endif
3626
3627   SCAN_PLAYFIELD(x, y)
3628   {
3629     Feld[x][y] = Last[x][y] = level.field[x][y];
3630     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3631     ChangeDelay[x][y] = 0;
3632     ChangePage[x][y] = -1;
3633     CustomValue[x][y] = 0;              // initialized in InitField()
3634     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3635     AmoebaNr[x][y] = 0;
3636     WasJustMoving[x][y] = 0;
3637     WasJustFalling[x][y] = 0;
3638     CheckCollision[x][y] = 0;
3639     CheckImpact[x][y] = 0;
3640     Stop[x][y] = FALSE;
3641     Pushed[x][y] = FALSE;
3642
3643     ChangeCount[x][y] = 0;
3644     ChangeEvent[x][y] = -1;
3645
3646     ExplodePhase[x][y] = 0;
3647     ExplodeDelay[x][y] = 0;
3648     ExplodeField[x][y] = EX_TYPE_NONE;
3649
3650     RunnerVisit[x][y] = 0;
3651     PlayerVisit[x][y] = 0;
3652
3653     GfxFrame[x][y] = 0;
3654     GfxRandom[x][y] = INIT_GFX_RANDOM();
3655     GfxElement[x][y] = EL_UNDEFINED;
3656     GfxAction[x][y] = ACTION_DEFAULT;
3657     GfxDir[x][y] = MV_NONE;
3658     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3659   }
3660
3661   SCAN_PLAYFIELD(x, y)
3662   {
3663     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3664       emulate_bd = FALSE;
3665     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3666       emulate_sb = FALSE;
3667     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3668       emulate_sp = FALSE;
3669
3670     InitField(x, y, TRUE);
3671
3672     ResetGfxAnimation(x, y);
3673   }
3674
3675   InitBeltMovement();
3676
3677   for (i = 0; i < MAX_PLAYERS; i++)
3678   {
3679     struct PlayerInfo *player = &stored_player[i];
3680
3681     // set number of special actions for bored and sleeping animation
3682     player->num_special_action_bored =
3683       get_num_special_action(player->artwork_element,
3684                              ACTION_BORING_1, ACTION_BORING_LAST);
3685     player->num_special_action_sleeping =
3686       get_num_special_action(player->artwork_element,
3687                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3688   }
3689
3690   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3691                     emulate_sb ? EMU_SOKOBAN :
3692                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3693
3694   // initialize type of slippery elements
3695   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3696   {
3697     if (!IS_CUSTOM_ELEMENT(i))
3698     {
3699       // default: elements slip down either to the left or right randomly
3700       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3701
3702       // SP style elements prefer to slip down on the left side
3703       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3704         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3705
3706       // BD style elements prefer to slip down on the left side
3707       if (game.emulation == EMU_BOULDERDASH)
3708         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3709     }
3710   }
3711
3712   // initialize explosion and ignition delay
3713   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3714   {
3715     if (!IS_CUSTOM_ELEMENT(i))
3716     {
3717       int num_phase = 8;
3718       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3719                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3720                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3721       int last_phase = (num_phase + 1) * delay;
3722       int half_phase = (num_phase / 2) * delay;
3723
3724       element_info[i].explosion_delay = last_phase - 1;
3725       element_info[i].ignition_delay = half_phase;
3726
3727       if (i == EL_BLACK_ORB)
3728         element_info[i].ignition_delay = 1;
3729     }
3730   }
3731
3732   // correct non-moving belts to start moving left
3733   for (i = 0; i < NUM_BELTS; i++)
3734     if (game.belt_dir[i] == MV_NONE)
3735       game.belt_dir_nr[i] = 3;          // not moving, next moving left
3736
3737 #if USE_NEW_PLAYER_ASSIGNMENTS
3738   for (i = 0; i < MAX_PLAYERS; i++)
3739   {
3740     stored_player[i].connected = FALSE;
3741
3742     // in network game mode, the local player might not be the first player
3743     if (stored_player[i].connected_locally)
3744       local_player = &stored_player[i];
3745   }
3746
3747   if (!network.enabled)
3748     local_player->connected = TRUE;
3749
3750   if (tape.playing)
3751   {
3752     for (i = 0; i < MAX_PLAYERS; i++)
3753       stored_player[i].connected = tape.player_participates[i];
3754   }
3755   else if (network.enabled)
3756   {
3757     // add team mode players connected over the network (needed for correct
3758     // assignment of player figures from level to locally playing players)
3759
3760     for (i = 0; i < MAX_PLAYERS; i++)
3761       if (stored_player[i].connected_network)
3762         stored_player[i].connected = TRUE;
3763   }
3764   else if (game.team_mode)
3765   {
3766     // try to guess locally connected team mode players (needed for correct
3767     // assignment of player figures from level to locally playing players)
3768
3769     for (i = 0; i < MAX_PLAYERS; i++)
3770       if (setup.input[i].use_joystick ||
3771           setup.input[i].key.left != KSYM_UNDEFINED)
3772         stored_player[i].connected = TRUE;
3773   }
3774
3775 #if DEBUG_INIT_PLAYER
3776   DebugPrintPlayerStatus("Player status after level initialization");
3777 #endif
3778
3779 #if DEBUG_INIT_PLAYER
3780   if (options.debug)
3781     printf("Reassigning players ...\n");
3782 #endif
3783
3784   // check if any connected player was not found in playfield
3785   for (i = 0; i < MAX_PLAYERS; i++)
3786   {
3787     struct PlayerInfo *player = &stored_player[i];
3788
3789     if (player->connected && !player->present)
3790     {
3791       struct PlayerInfo *field_player = NULL;
3792
3793 #if DEBUG_INIT_PLAYER
3794       if (options.debug)
3795         printf("- looking for field player for player %d ...\n", i + 1);
3796 #endif
3797
3798       // assign first free player found that is present in the playfield
3799
3800       // first try: look for unmapped playfield player that is not connected
3801       for (j = 0; j < MAX_PLAYERS; j++)
3802         if (field_player == NULL &&
3803             stored_player[j].present &&
3804             !stored_player[j].mapped &&
3805             !stored_player[j].connected)
3806           field_player = &stored_player[j];
3807
3808       // second try: look for *any* unmapped playfield player
3809       for (j = 0; j < MAX_PLAYERS; j++)
3810         if (field_player == NULL &&
3811             stored_player[j].present &&
3812             !stored_player[j].mapped)
3813           field_player = &stored_player[j];
3814
3815       if (field_player != NULL)
3816       {
3817         int jx = field_player->jx, jy = field_player->jy;
3818
3819 #if DEBUG_INIT_PLAYER
3820         if (options.debug)
3821           printf("- found player %d\n", field_player->index_nr + 1);
3822 #endif
3823
3824         player->present = FALSE;
3825         player->active = FALSE;
3826
3827         field_player->present = TRUE;
3828         field_player->active = TRUE;
3829
3830         /*
3831         player->initial_element = field_player->initial_element;
3832         player->artwork_element = field_player->artwork_element;
3833
3834         player->block_last_field       = field_player->block_last_field;
3835         player->block_delay_adjustment = field_player->block_delay_adjustment;
3836         */
3837
3838         StorePlayer[jx][jy] = field_player->element_nr;
3839
3840         field_player->jx = field_player->last_jx = jx;
3841         field_player->jy = field_player->last_jy = jy;
3842
3843         if (local_player == player)
3844           local_player = field_player;
3845
3846         map_player_action[field_player->index_nr] = i;
3847
3848         field_player->mapped = TRUE;
3849
3850 #if DEBUG_INIT_PLAYER
3851         if (options.debug)
3852           printf("- map_player_action[%d] == %d\n",
3853                  field_player->index_nr + 1, i + 1);
3854 #endif
3855       }
3856     }
3857
3858     if (player->connected && player->present)
3859       player->mapped = TRUE;
3860   }
3861
3862 #if DEBUG_INIT_PLAYER
3863   DebugPrintPlayerStatus("Player status after player assignment (first stage)");
3864 #endif
3865
3866 #else
3867
3868   // check if any connected player was not found in playfield
3869   for (i = 0; i < MAX_PLAYERS; i++)
3870   {
3871     struct PlayerInfo *player = &stored_player[i];
3872
3873     if (player->connected && !player->present)
3874     {
3875       for (j = 0; j < MAX_PLAYERS; j++)
3876       {
3877         struct PlayerInfo *field_player = &stored_player[j];
3878         int jx = field_player->jx, jy = field_player->jy;
3879
3880         // assign first free player found that is present in the playfield
3881         if (field_player->present && !field_player->connected)
3882         {
3883           player->present = TRUE;
3884           player->active = TRUE;
3885
3886           field_player->present = FALSE;
3887           field_player->active = FALSE;
3888
3889           player->initial_element = field_player->initial_element;
3890           player->artwork_element = field_player->artwork_element;
3891
3892           player->block_last_field       = field_player->block_last_field;
3893           player->block_delay_adjustment = field_player->block_delay_adjustment;
3894
3895           StorePlayer[jx][jy] = player->element_nr;
3896
3897           player->jx = player->last_jx = jx;
3898           player->jy = player->last_jy = jy;
3899
3900           break;
3901         }
3902       }
3903     }
3904   }
3905 #endif
3906
3907 #if 0
3908   printf("::: local_player->present == %d\n", local_player->present);
3909 #endif
3910
3911   // set focus to local player for network games, else to all players
3912   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3913   game.centered_player_nr_next = game.centered_player_nr;
3914   game.set_centered_player = FALSE;
3915
3916   if (network_playing && tape.recording)
3917   {
3918     // store client dependent player focus when recording network games
3919     tape.centered_player_nr_next = game.centered_player_nr_next;
3920     tape.set_centered_player = TRUE;
3921   }
3922
3923   if (tape.playing)
3924   {
3925     // when playing a tape, eliminate all players who do not participate
3926
3927 #if USE_NEW_PLAYER_ASSIGNMENTS
3928
3929     if (!game.team_mode)
3930     {
3931       for (i = 0; i < MAX_PLAYERS; i++)
3932       {
3933         if (stored_player[i].active &&
3934             !tape.player_participates[map_player_action[i]])
3935         {
3936           struct PlayerInfo *player = &stored_player[i];
3937           int jx = player->jx, jy = player->jy;
3938
3939 #if DEBUG_INIT_PLAYER
3940           if (options.debug)
3941             printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
3942 #endif
3943
3944           player->active = FALSE;
3945           StorePlayer[jx][jy] = 0;
3946           Feld[jx][jy] = EL_EMPTY;
3947         }
3948       }
3949     }
3950
3951 #else
3952
3953     for (i = 0; i < MAX_PLAYERS; i++)
3954     {
3955       if (stored_player[i].active &&
3956           !tape.player_participates[i])
3957       {
3958         struct PlayerInfo *player = &stored_player[i];
3959         int jx = player->jx, jy = player->jy;
3960
3961         player->active = FALSE;
3962         StorePlayer[jx][jy] = 0;
3963         Feld[jx][jy] = EL_EMPTY;
3964       }
3965     }
3966 #endif
3967   }
3968   else if (!network.enabled && !game.team_mode)         // && !tape.playing
3969   {
3970     // when in single player mode, eliminate all but the local player
3971
3972     for (i = 0; i < MAX_PLAYERS; i++)
3973     {
3974       struct PlayerInfo *player = &stored_player[i];
3975
3976       if (player->active && player != local_player)
3977       {
3978         int jx = player->jx, jy = player->jy;
3979
3980         player->active = FALSE;
3981         player->present = FALSE;
3982
3983         StorePlayer[jx][jy] = 0;
3984         Feld[jx][jy] = EL_EMPTY;
3985       }
3986     }
3987   }
3988
3989   for (i = 0; i < MAX_PLAYERS; i++)
3990     if (stored_player[i].active)
3991       game.players_still_needed++;
3992
3993   if (level.solved_by_one_player)
3994     game.players_still_needed = 1;
3995
3996   // when recording the game, store which players take part in the game
3997   if (tape.recording)
3998   {
3999 #if USE_NEW_PLAYER_ASSIGNMENTS
4000     for (i = 0; i < MAX_PLAYERS; i++)
4001       if (stored_player[i].connected)
4002         tape.player_participates[i] = TRUE;
4003 #else
4004     for (i = 0; i < MAX_PLAYERS; i++)
4005       if (stored_player[i].active)
4006         tape.player_participates[i] = TRUE;
4007 #endif
4008   }
4009
4010 #if DEBUG_INIT_PLAYER
4011   DebugPrintPlayerStatus("Player status after player assignment (final stage)");
4012 #endif
4013
4014   if (BorderElement == EL_EMPTY)
4015   {
4016     SBX_Left = 0;
4017     SBX_Right = lev_fieldx - SCR_FIELDX;
4018     SBY_Upper = 0;
4019     SBY_Lower = lev_fieldy - SCR_FIELDY;
4020   }
4021   else
4022   {
4023     SBX_Left = -1;
4024     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4025     SBY_Upper = -1;
4026     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4027   }
4028
4029   if (full_lev_fieldx <= SCR_FIELDX)
4030     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4031   if (full_lev_fieldy <= SCR_FIELDY)
4032     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4033
4034   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4035     SBX_Left--;
4036   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4037     SBY_Upper--;
4038
4039   // if local player not found, look for custom element that might create
4040   // the player (make some assumptions about the right custom element)
4041   if (!local_player->present)
4042   {
4043     int start_x = 0, start_y = 0;
4044     int found_rating = 0;
4045     int found_element = EL_UNDEFINED;
4046     int player_nr = local_player->index_nr;
4047
4048     SCAN_PLAYFIELD(x, y)
4049     {
4050       int element = Feld[x][y];
4051       int content;
4052       int xx, yy;
4053       boolean is_player;
4054
4055       if (level.use_start_element[player_nr] &&
4056           level.start_element[player_nr] == element &&
4057           found_rating < 4)
4058       {
4059         start_x = x;
4060         start_y = y;
4061
4062         found_rating = 4;
4063         found_element = element;
4064       }
4065
4066       if (!IS_CUSTOM_ELEMENT(element))
4067         continue;
4068
4069       if (CAN_CHANGE(element))
4070       {
4071         for (i = 0; i < element_info[element].num_change_pages; i++)
4072         {
4073           // check for player created from custom element as single target
4074           content = element_info[element].change_page[i].target_element;
4075           is_player = ELEM_IS_PLAYER(content);
4076
4077           if (is_player && (found_rating < 3 ||
4078                             (found_rating == 3 && element < found_element)))
4079           {
4080             start_x = x;
4081             start_y = y;
4082
4083             found_rating = 3;
4084             found_element = element;
4085           }
4086         }
4087       }
4088
4089       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4090       {
4091         // check for player created from custom element as explosion content
4092         content = element_info[element].content.e[xx][yy];
4093         is_player = ELEM_IS_PLAYER(content);
4094
4095         if (is_player && (found_rating < 2 ||
4096                           (found_rating == 2 && element < found_element)))
4097         {
4098           start_x = x + xx - 1;
4099           start_y = y + yy - 1;
4100
4101           found_rating = 2;
4102           found_element = element;
4103         }
4104
4105         if (!CAN_CHANGE(element))
4106           continue;
4107
4108         for (i = 0; i < element_info[element].num_change_pages; i++)
4109         {
4110           // check for player created from custom element as extended target
4111           content =
4112             element_info[element].change_page[i].target_content.e[xx][yy];
4113
4114           is_player = ELEM_IS_PLAYER(content);
4115
4116           if (is_player && (found_rating < 1 ||
4117                             (found_rating == 1 && element < found_element)))
4118           {
4119             start_x = x + xx - 1;
4120             start_y = y + yy - 1;
4121
4122             found_rating = 1;
4123             found_element = element;
4124           }
4125         }
4126       }
4127     }
4128
4129     scroll_x = SCROLL_POSITION_X(start_x);
4130     scroll_y = SCROLL_POSITION_Y(start_y);
4131   }
4132   else
4133   {
4134     scroll_x = SCROLL_POSITION_X(local_player->jx);
4135     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4136   }
4137
4138   // !!! FIX THIS (START) !!!
4139   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4140   {
4141     InitGameEngine_EM();
4142   }
4143   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4144   {
4145     InitGameEngine_SP();
4146   }
4147   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4148   {
4149     InitGameEngine_MM();
4150   }
4151   else
4152   {
4153     DrawLevel(REDRAW_FIELD);
4154     DrawAllPlayers();
4155
4156     // after drawing the level, correct some elements
4157     if (game.timegate_time_left == 0)
4158       CloseAllOpenTimegates();
4159   }
4160
4161   // blit playfield from scroll buffer to normal back buffer for fading in
4162   BlitScreenToBitmap(backbuffer);
4163   // !!! FIX THIS (END) !!!
4164
4165   DrawMaskedBorder(fade_mask);
4166
4167   FadeIn(fade_mask);
4168
4169 #if 1
4170   // full screen redraw is required at this point in the following cases:
4171   // - special editor door undrawn when game was started from level editor
4172   // - drawing area (playfield) was changed and has to be removed completely
4173   redraw_mask = REDRAW_ALL;
4174   BackToFront();
4175 #endif
4176
4177   if (!game.restart_level)
4178   {
4179     // copy default game door content to main double buffer
4180
4181     // !!! CHECK AGAIN !!!
4182     SetPanelBackground();
4183     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4184     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4185   }
4186
4187   SetPanelBackground();
4188   SetDrawBackgroundMask(REDRAW_DOOR_1);
4189
4190   UpdateAndDisplayGameControlValues();
4191
4192   if (!game.restart_level)
4193   {
4194     UnmapGameButtons();
4195     UnmapTapeButtons();
4196
4197     FreeGameButtons();
4198     CreateGameButtons();
4199
4200     MapGameButtons();
4201     MapTapeButtons();
4202
4203     // copy actual game door content to door double buffer for OpenDoor()
4204     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4205
4206     OpenDoor(DOOR_OPEN_ALL);
4207
4208     KeyboardAutoRepeatOffUnlessAutoplay();
4209
4210 #if DEBUG_INIT_PLAYER
4211     DebugPrintPlayerStatus("Player status (final)");
4212 #endif
4213   }
4214
4215   UnmapAllGadgets();
4216
4217   MapGameButtons();
4218   MapTapeButtons();
4219
4220   if (!game.restart_level && !tape.playing)
4221   {
4222     LevelStats_incPlayed(level_nr);
4223
4224     SaveLevelSetup_SeriesInfo();
4225   }
4226
4227   game.restart_level = FALSE;
4228   game.restart_game_message = NULL;
4229   game.request_active = FALSE;
4230
4231   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4232     InitGameActions_MM();
4233
4234   SaveEngineSnapshotToListInitial();
4235
4236   if (!game.restart_level)
4237   {
4238     PlaySound(SND_GAME_STARTING);
4239
4240     if (setup.sound_music)
4241       PlayLevelMusic();
4242   }
4243 }
4244
4245 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4246                         int actual_player_x, int actual_player_y)
4247 {
4248   // this is used for non-R'n'D game engines to update certain engine values
4249
4250   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4251   {
4252     actual_player_x = correctLevelPosX_EM(actual_player_x);
4253     actual_player_y = correctLevelPosY_EM(actual_player_y);
4254   }
4255
4256   // needed to determine if sounds are played within the visible screen area
4257   scroll_x = actual_scroll_x;
4258   scroll_y = actual_scroll_y;
4259
4260   // needed to get player position for "follow finger" playing input method
4261   local_player->jx = actual_player_x;
4262   local_player->jy = actual_player_y;
4263 }
4264
4265 void InitMovDir(int x, int y)
4266 {
4267   int i, element = Feld[x][y];
4268   static int xy[4][2] =
4269   {
4270     {  0, +1 },
4271     { +1,  0 },
4272     {  0, -1 },
4273     { -1,  0 }
4274   };
4275   static int direction[3][4] =
4276   {
4277     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4278     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4279     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4280   };
4281
4282   switch (element)
4283   {
4284     case EL_BUG_RIGHT:
4285     case EL_BUG_UP:
4286     case EL_BUG_LEFT:
4287     case EL_BUG_DOWN:
4288       Feld[x][y] = EL_BUG;
4289       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4290       break;
4291
4292     case EL_SPACESHIP_RIGHT:
4293     case EL_SPACESHIP_UP:
4294     case EL_SPACESHIP_LEFT:
4295     case EL_SPACESHIP_DOWN:
4296       Feld[x][y] = EL_SPACESHIP;
4297       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4298       break;
4299
4300     case EL_BD_BUTTERFLY_RIGHT:
4301     case EL_BD_BUTTERFLY_UP:
4302     case EL_BD_BUTTERFLY_LEFT:
4303     case EL_BD_BUTTERFLY_DOWN:
4304       Feld[x][y] = EL_BD_BUTTERFLY;
4305       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4306       break;
4307
4308     case EL_BD_FIREFLY_RIGHT:
4309     case EL_BD_FIREFLY_UP:
4310     case EL_BD_FIREFLY_LEFT:
4311     case EL_BD_FIREFLY_DOWN:
4312       Feld[x][y] = EL_BD_FIREFLY;
4313       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4314       break;
4315
4316     case EL_PACMAN_RIGHT:
4317     case EL_PACMAN_UP:
4318     case EL_PACMAN_LEFT:
4319     case EL_PACMAN_DOWN:
4320       Feld[x][y] = EL_PACMAN;
4321       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4322       break;
4323
4324     case EL_YAMYAM_LEFT:
4325     case EL_YAMYAM_RIGHT:
4326     case EL_YAMYAM_UP:
4327     case EL_YAMYAM_DOWN:
4328       Feld[x][y] = EL_YAMYAM;
4329       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4330       break;
4331
4332     case EL_SP_SNIKSNAK:
4333       MovDir[x][y] = MV_UP;
4334       break;
4335
4336     case EL_SP_ELECTRON:
4337       MovDir[x][y] = MV_LEFT;
4338       break;
4339
4340     case EL_MOLE_LEFT:
4341     case EL_MOLE_RIGHT:
4342     case EL_MOLE_UP:
4343     case EL_MOLE_DOWN:
4344       Feld[x][y] = EL_MOLE;
4345       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4346       break;
4347
4348     default:
4349       if (IS_CUSTOM_ELEMENT(element))
4350       {
4351         struct ElementInfo *ei = &element_info[element];
4352         int move_direction_initial = ei->move_direction_initial;
4353         int move_pattern = ei->move_pattern;
4354
4355         if (move_direction_initial == MV_START_PREVIOUS)
4356         {
4357           if (MovDir[x][y] != MV_NONE)
4358             return;
4359
4360           move_direction_initial = MV_START_AUTOMATIC;
4361         }
4362
4363         if (move_direction_initial == MV_START_RANDOM)
4364           MovDir[x][y] = 1 << RND(4);
4365         else if (move_direction_initial & MV_ANY_DIRECTION)
4366           MovDir[x][y] = move_direction_initial;
4367         else if (move_pattern == MV_ALL_DIRECTIONS ||
4368                  move_pattern == MV_TURNING_LEFT ||
4369                  move_pattern == MV_TURNING_RIGHT ||
4370                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4371                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4372                  move_pattern == MV_TURNING_RANDOM)
4373           MovDir[x][y] = 1 << RND(4);
4374         else if (move_pattern == MV_HORIZONTAL)
4375           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4376         else if (move_pattern == MV_VERTICAL)
4377           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4378         else if (move_pattern & MV_ANY_DIRECTION)
4379           MovDir[x][y] = element_info[element].move_pattern;
4380         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4381                  move_pattern == MV_ALONG_RIGHT_SIDE)
4382         {
4383           // use random direction as default start direction
4384           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4385             MovDir[x][y] = 1 << RND(4);
4386
4387           for (i = 0; i < NUM_DIRECTIONS; i++)
4388           {
4389             int x1 = x + xy[i][0];
4390             int y1 = y + xy[i][1];
4391
4392             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4393             {
4394               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4395                 MovDir[x][y] = direction[0][i];
4396               else
4397                 MovDir[x][y] = direction[1][i];
4398
4399               break;
4400             }
4401           }
4402         }                
4403       }
4404       else
4405       {
4406         MovDir[x][y] = 1 << RND(4);
4407
4408         if (element != EL_BUG &&
4409             element != EL_SPACESHIP &&
4410             element != EL_BD_BUTTERFLY &&
4411             element != EL_BD_FIREFLY)
4412           break;
4413
4414         for (i = 0; i < NUM_DIRECTIONS; i++)
4415         {
4416           int x1 = x + xy[i][0];
4417           int y1 = y + xy[i][1];
4418
4419           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4420           {
4421             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4422             {
4423               MovDir[x][y] = direction[0][i];
4424               break;
4425             }
4426             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4427                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4428             {
4429               MovDir[x][y] = direction[1][i];
4430               break;
4431             }
4432           }
4433         }
4434       }
4435       break;
4436   }
4437
4438   GfxDir[x][y] = MovDir[x][y];
4439 }
4440
4441 void InitAmoebaNr(int x, int y)
4442 {
4443   int i;
4444   int group_nr = AmoebeNachbarNr(x, y);
4445
4446   if (group_nr == 0)
4447   {
4448     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4449     {
4450       if (AmoebaCnt[i] == 0)
4451       {
4452         group_nr = i;
4453         break;
4454       }
4455     }
4456   }
4457
4458   AmoebaNr[x][y] = group_nr;
4459   AmoebaCnt[group_nr]++;
4460   AmoebaCnt2[group_nr]++;
4461 }
4462
4463 static void LevelSolved(void)
4464 {
4465   if (level.game_engine_type == GAME_ENGINE_TYPE_RND &&
4466       game.players_still_needed > 0)
4467     return;
4468
4469   game.LevelSolved = TRUE;
4470   game.GameOver = TRUE;
4471
4472   game.score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4473                       level.native_em_level->lev->score :
4474                       level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4475                       game_mm.score :
4476                       game.score);
4477   game.health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4478                        MM_HEALTH(game_mm.laser_overload_value) :
4479                        game.health);
4480
4481   game.LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed : TimeLeft);
4482   game.LevelSolved_CountingScore = game.score_final;
4483   game.LevelSolved_CountingHealth = game.health_final;
4484 }
4485
4486 void GameWon(void)
4487 {
4488   static int time_count_steps;
4489   static int time, time_final;
4490   static int score, score_final;
4491   static int health, health_final;
4492   static int game_over_delay_1 = 0;
4493   static int game_over_delay_2 = 0;
4494   static int game_over_delay_3 = 0;
4495   int game_over_delay_value_1 = 50;
4496   int game_over_delay_value_2 = 25;
4497   int game_over_delay_value_3 = 50;
4498
4499   if (!game.LevelSolved_GameWon)
4500   {
4501     int i;
4502
4503     // do not start end game actions before the player stops moving (to exit)
4504     if (local_player->MovPos)
4505       return;
4506
4507     game.LevelSolved_GameWon = TRUE;
4508     game.LevelSolved_SaveTape = tape.recording;
4509     game.LevelSolved_SaveScore = !tape.playing;
4510
4511     if (!tape.playing)
4512     {
4513       LevelStats_incSolved(level_nr);
4514
4515       SaveLevelSetup_SeriesInfo();
4516     }
4517
4518     if (tape.auto_play)         // tape might already be stopped here
4519       tape.auto_play_level_solved = TRUE;
4520
4521     TapeStop();
4522
4523     game_over_delay_1 = 0;
4524     game_over_delay_2 = 0;
4525     game_over_delay_3 = game_over_delay_value_3;
4526
4527     time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4528     score = score_final = game.score_final;
4529     health = health_final = game.health_final;
4530
4531     if (level.score[SC_TIME_BONUS] > 0)
4532     {
4533       if (TimeLeft > 0)
4534       {
4535         time_final = 0;
4536         score_final += TimeLeft * level.score[SC_TIME_BONUS];
4537       }
4538       else if (game.no_time_limit && TimePlayed < 999)
4539       {
4540         time_final = 999;
4541         score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4542       }
4543
4544       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4545
4546       game_over_delay_1 = game_over_delay_value_1;
4547
4548       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4549       {
4550         health_final = 0;
4551         score_final += health * level.score[SC_TIME_BONUS];
4552
4553         game_over_delay_2 = game_over_delay_value_2;
4554       }
4555
4556       game.score_final = score_final;
4557       game.health_final = health_final;
4558     }
4559
4560     if (level_editor_test_game)
4561     {
4562       time = time_final;
4563       score = score_final;
4564
4565       game.LevelSolved_CountingTime = time;
4566       game.LevelSolved_CountingScore = score;
4567
4568       game_panel_controls[GAME_PANEL_TIME].value = time;
4569       game_panel_controls[GAME_PANEL_SCORE].value = score;
4570
4571       DisplayGameControlValues();
4572     }
4573
4574     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4575     {
4576       // check if last player has left the level
4577       if (game.exit_x >= 0 &&
4578           game.exit_y >= 0)
4579       {
4580         int x = game.exit_x;
4581         int y = game.exit_y;
4582         int element = Feld[x][y];
4583
4584         // close exit door after last player
4585         if ((game.all_players_gone &&
4586              (element == EL_EXIT_OPEN ||
4587               element == EL_SP_EXIT_OPEN ||
4588               element == EL_STEEL_EXIT_OPEN)) ||
4589             element == EL_EM_EXIT_OPEN ||
4590             element == EL_EM_STEEL_EXIT_OPEN)
4591         {
4592
4593           Feld[x][y] =
4594             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4595              element == EL_EM_EXIT_OPEN         ? EL_EM_EXIT_CLOSING :
4596              element == EL_SP_EXIT_OPEN         ? EL_SP_EXIT_CLOSING:
4597              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4598              EL_EM_STEEL_EXIT_CLOSING);
4599
4600           PlayLevelSoundElementAction(x, y, element, ACTION_CLOSING);
4601         }
4602
4603         // player disappears
4604         DrawLevelField(x, y);
4605       }
4606
4607       for (i = 0; i < MAX_PLAYERS; i++)
4608       {
4609         struct PlayerInfo *player = &stored_player[i];
4610
4611         if (player->present)
4612         {
4613           RemovePlayer(player);
4614
4615           // player disappears
4616           DrawLevelField(player->jx, player->jy);
4617         }
4618       }
4619     }
4620
4621     PlaySound(SND_GAME_WINNING);
4622   }
4623
4624   if (game_over_delay_1 > 0)
4625   {
4626     game_over_delay_1--;
4627
4628     return;
4629   }
4630
4631   if (time != time_final)
4632   {
4633     int time_to_go = ABS(time_final - time);
4634     int time_count_dir = (time < time_final ? +1 : -1);
4635
4636     if (time_to_go < time_count_steps)
4637       time_count_steps = 1;
4638
4639     time  += time_count_steps * time_count_dir;
4640     score += time_count_steps * level.score[SC_TIME_BONUS];
4641
4642     game.LevelSolved_CountingTime = time;
4643     game.LevelSolved_CountingScore = score;
4644
4645     game_panel_controls[GAME_PANEL_TIME].value = time;
4646     game_panel_controls[GAME_PANEL_SCORE].value = score;
4647
4648     DisplayGameControlValues();
4649
4650     if (time == time_final)
4651       StopSound(SND_GAME_LEVELTIME_BONUS);
4652     else if (setup.sound_loops)
4653       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4654     else
4655       PlaySound(SND_GAME_LEVELTIME_BONUS);
4656
4657     return;
4658   }
4659
4660   if (game_over_delay_2 > 0)
4661   {
4662     game_over_delay_2--;
4663
4664     return;
4665   }
4666
4667   if (health != health_final)
4668   {
4669     int health_count_dir = (health < health_final ? +1 : -1);
4670
4671     health += health_count_dir;
4672     score  += level.score[SC_TIME_BONUS];
4673
4674     game.LevelSolved_CountingHealth = health;
4675     game.LevelSolved_CountingScore = score;
4676
4677     game_panel_controls[GAME_PANEL_HEALTH].value = health;
4678     game_panel_controls[GAME_PANEL_SCORE].value = score;
4679
4680     DisplayGameControlValues();
4681
4682     if (health == health_final)
4683       StopSound(SND_GAME_LEVELTIME_BONUS);
4684     else if (setup.sound_loops)
4685       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4686     else
4687       PlaySound(SND_GAME_LEVELTIME_BONUS);
4688
4689     return;
4690   }
4691
4692   game.panel.active = FALSE;
4693
4694   if (game_over_delay_3 > 0)
4695   {
4696     game_over_delay_3--;
4697
4698     return;
4699   }
4700
4701   GameEnd();
4702 }
4703
4704 void GameEnd(void)
4705 {
4706   // used instead of "level_nr" (needed for network games)
4707   int last_level_nr = levelset.level_nr;
4708   int hi_pos;
4709
4710   game.LevelSolved_GameEnd = TRUE;
4711
4712   if (game.LevelSolved_SaveTape)
4713   {
4714     // make sure that request dialog to save tape does not open door again
4715     if (!global.use_envelope_request)
4716       CloseDoor(DOOR_CLOSE_1);
4717
4718     SaveTapeChecked_LevelSolved(tape.level_nr);         // ask to save tape
4719   }
4720
4721   // if no tape is to be saved, close both doors simultaneously
4722   CloseDoor(DOOR_CLOSE_ALL);
4723
4724   if (level_editor_test_game)
4725   {
4726     SetGameStatus(GAME_MODE_MAIN);
4727
4728     DrawMainMenu();
4729
4730     return;
4731   }
4732
4733   if (!game.LevelSolved_SaveScore)
4734   {
4735     SetGameStatus(GAME_MODE_MAIN);
4736
4737     DrawMainMenu();
4738
4739     return;
4740   }
4741
4742   if (level_nr == leveldir_current->handicap_level)
4743   {
4744     leveldir_current->handicap_level++;
4745
4746     SaveLevelSetup_SeriesInfo();
4747   }
4748
4749   if (setup.increment_levels &&
4750       level_nr < leveldir_current->last_level &&
4751       !network_playing)
4752   {
4753     level_nr++;         // advance to next level
4754     TapeErase();        // start with empty tape
4755
4756     if (setup.auto_play_next_level)
4757     {
4758       LoadLevel(level_nr);
4759
4760       SaveLevelSetup_SeriesInfo();
4761     }
4762   }
4763
4764   hi_pos = NewHiScore(last_level_nr);
4765
4766   if (hi_pos >= 0 && !setup.skip_scores_after_game)
4767   {
4768     SetGameStatus(GAME_MODE_SCORES);
4769
4770     DrawHallOfFame(last_level_nr, hi_pos);
4771   }
4772   else if (setup.auto_play_next_level && setup.increment_levels &&
4773            last_level_nr < leveldir_current->last_level &&
4774            !network_playing)
4775   {
4776     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
4777   }
4778   else
4779   {
4780     SetGameStatus(GAME_MODE_MAIN);
4781
4782     DrawMainMenu();
4783   }
4784 }
4785
4786 int NewHiScore(int level_nr)
4787 {
4788   int k, l;
4789   int position = -1;
4790   boolean one_score_entry_per_name = !program.many_scores_per_name;
4791
4792   LoadScore(level_nr);
4793
4794   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4795       game.score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
4796     return -1;
4797
4798   for (k = 0; k < MAX_SCORE_ENTRIES; k++)
4799   {
4800     if (game.score_final > highscore[k].Score)
4801     {
4802       // player has made it to the hall of fame
4803
4804       if (k < MAX_SCORE_ENTRIES - 1)
4805       {
4806         int m = MAX_SCORE_ENTRIES - 1;
4807
4808         if (one_score_entry_per_name)
4809         {
4810           for (l = k; l < MAX_SCORE_ENTRIES; l++)
4811             if (strEqual(setup.player_name, highscore[l].Name))
4812               m = l;
4813
4814           if (m == k)   // player's new highscore overwrites his old one
4815             goto put_into_list;
4816         }
4817
4818         for (l = m; l > k; l--)
4819         {
4820           strcpy(highscore[l].Name, highscore[l - 1].Name);
4821           highscore[l].Score = highscore[l - 1].Score;
4822         }
4823       }
4824
4825       put_into_list:
4826
4827       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4828       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4829       highscore[k].Score = game.score_final;
4830       position = k;
4831
4832       break;
4833     }
4834     else if (one_score_entry_per_name &&
4835              !strncmp(setup.player_name, highscore[k].Name,
4836                       MAX_PLAYER_NAME_LEN))
4837       break;    // player already there with a higher score
4838   }
4839
4840   if (position >= 0) 
4841     SaveScore(level_nr);
4842
4843   return position;
4844 }
4845
4846 static int getElementMoveStepsizeExt(int x, int y, int direction)
4847 {
4848   int element = Feld[x][y];
4849   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4850   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4851   int horiz_move = (dx != 0);
4852   int sign = (horiz_move ? dx : dy);
4853   int step = sign * element_info[element].move_stepsize;
4854
4855   // special values for move stepsize for spring and things on conveyor belt
4856   if (horiz_move)
4857   {
4858     if (CAN_FALL(element) &&
4859         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4860       step = sign * MOVE_STEPSIZE_NORMAL / 2;
4861     else if (element == EL_SPRING)
4862       step = sign * MOVE_STEPSIZE_NORMAL * 2;
4863   }
4864
4865   return step;
4866 }
4867
4868 static int getElementMoveStepsize(int x, int y)
4869 {
4870   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4871 }
4872
4873 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4874 {
4875   if (player->GfxAction != action || player->GfxDir != dir)
4876   {
4877     player->GfxAction = action;
4878     player->GfxDir = dir;
4879     player->Frame = 0;
4880     player->StepFrame = 0;
4881   }
4882 }
4883
4884 static void ResetGfxFrame(int x, int y)
4885 {
4886   // profiling showed that "autotest" spends 10~20% of its time in this function
4887   if (DrawingDeactivatedField())
4888     return;
4889
4890   int element = Feld[x][y];
4891   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4892
4893   if (graphic_info[graphic].anim_global_sync)
4894     GfxFrame[x][y] = FrameCounter;
4895   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4896     GfxFrame[x][y] = CustomValue[x][y];
4897   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4898     GfxFrame[x][y] = element_info[element].collect_score;
4899   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4900     GfxFrame[x][y] = ChangeDelay[x][y];
4901 }
4902
4903 static void ResetGfxAnimation(int x, int y)
4904 {
4905   GfxAction[x][y] = ACTION_DEFAULT;
4906   GfxDir[x][y] = MovDir[x][y];
4907   GfxFrame[x][y] = 0;
4908
4909   ResetGfxFrame(x, y);
4910 }
4911
4912 static void ResetRandomAnimationValue(int x, int y)
4913 {
4914   GfxRandom[x][y] = INIT_GFX_RANDOM();
4915 }
4916
4917 static void InitMovingField(int x, int y, int direction)
4918 {
4919   int element = Feld[x][y];
4920   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4921   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4922   int newx = x + dx;
4923   int newy = y + dy;
4924   boolean is_moving_before, is_moving_after;
4925
4926   // check if element was/is moving or being moved before/after mode change
4927   is_moving_before = (WasJustMoving[x][y] != 0);
4928   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
4929
4930   // reset animation only for moving elements which change direction of moving
4931   // or which just started or stopped moving
4932   // (else CEs with property "can move" / "not moving" are reset each frame)
4933   if (is_moving_before != is_moving_after ||
4934       direction != MovDir[x][y])
4935     ResetGfxAnimation(x, y);
4936
4937   MovDir[x][y] = direction;
4938   GfxDir[x][y] = direction;
4939
4940   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4941                      direction == MV_DOWN && CAN_FALL(element) ?
4942                      ACTION_FALLING : ACTION_MOVING);
4943
4944   // this is needed for CEs with property "can move" / "not moving"
4945
4946   if (is_moving_after)
4947   {
4948     if (Feld[newx][newy] == EL_EMPTY)
4949       Feld[newx][newy] = EL_BLOCKED;
4950
4951     MovDir[newx][newy] = MovDir[x][y];
4952
4953     CustomValue[newx][newy] = CustomValue[x][y];
4954
4955     GfxFrame[newx][newy] = GfxFrame[x][y];
4956     GfxRandom[newx][newy] = GfxRandom[x][y];
4957     GfxAction[newx][newy] = GfxAction[x][y];
4958     GfxDir[newx][newy] = GfxDir[x][y];
4959   }
4960 }
4961
4962 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4963 {
4964   int direction = MovDir[x][y];
4965   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4966   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
4967
4968   *goes_to_x = newx;
4969   *goes_to_y = newy;
4970 }
4971
4972 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4973 {
4974   int oldx = x, oldy = y;
4975   int direction = MovDir[x][y];
4976
4977   if (direction == MV_LEFT)
4978     oldx++;
4979   else if (direction == MV_RIGHT)
4980     oldx--;
4981   else if (direction == MV_UP)
4982     oldy++;
4983   else if (direction == MV_DOWN)
4984     oldy--;
4985
4986   *comes_from_x = oldx;
4987   *comes_from_y = oldy;
4988 }
4989
4990 static int MovingOrBlocked2Element(int x, int y)
4991 {
4992   int element = Feld[x][y];
4993
4994   if (element == EL_BLOCKED)
4995   {
4996     int oldx, oldy;
4997
4998     Blocked2Moving(x, y, &oldx, &oldy);
4999     return Feld[oldx][oldy];
5000   }
5001   else
5002     return element;
5003 }
5004
5005 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5006 {
5007   // like MovingOrBlocked2Element(), but if element is moving
5008   // and (x,y) is the field the moving element is just leaving,
5009   // return EL_BLOCKED instead of the element value
5010   int element = Feld[x][y];
5011
5012   if (IS_MOVING(x, y))
5013   {
5014     if (element == EL_BLOCKED)
5015     {
5016       int oldx, oldy;
5017
5018       Blocked2Moving(x, y, &oldx, &oldy);
5019       return Feld[oldx][oldy];
5020     }
5021     else
5022       return EL_BLOCKED;
5023   }
5024   else
5025     return element;
5026 }
5027
5028 static void RemoveField(int x, int y)
5029 {
5030   Feld[x][y] = EL_EMPTY;
5031
5032   MovPos[x][y] = 0;
5033   MovDir[x][y] = 0;
5034   MovDelay[x][y] = 0;
5035
5036   CustomValue[x][y] = 0;
5037
5038   AmoebaNr[x][y] = 0;
5039   ChangeDelay[x][y] = 0;
5040   ChangePage[x][y] = -1;
5041   Pushed[x][y] = FALSE;
5042
5043   GfxElement[x][y] = EL_UNDEFINED;
5044   GfxAction[x][y] = ACTION_DEFAULT;
5045   GfxDir[x][y] = MV_NONE;
5046 }
5047
5048 static void RemoveMovingField(int x, int y)
5049 {
5050   int oldx = x, oldy = y, newx = x, newy = y;
5051   int element = Feld[x][y];
5052   int next_element = EL_UNDEFINED;
5053
5054   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5055     return;
5056
5057   if (IS_MOVING(x, y))
5058   {
5059     Moving2Blocked(x, y, &newx, &newy);
5060
5061     if (Feld[newx][newy] != EL_BLOCKED)
5062     {
5063       // element is moving, but target field is not free (blocked), but
5064       // already occupied by something different (example: acid pool);
5065       // in this case, only remove the moving field, but not the target
5066
5067       RemoveField(oldx, oldy);
5068
5069       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5070
5071       TEST_DrawLevelField(oldx, oldy);
5072
5073       return;
5074     }
5075   }
5076   else if (element == EL_BLOCKED)
5077   {
5078     Blocked2Moving(x, y, &oldx, &oldy);
5079     if (!IS_MOVING(oldx, oldy))
5080       return;
5081   }
5082
5083   if (element == EL_BLOCKED &&
5084       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5085        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5086        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5087        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5088        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5089        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5090     next_element = get_next_element(Feld[oldx][oldy]);
5091
5092   RemoveField(oldx, oldy);
5093   RemoveField(newx, newy);
5094
5095   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5096
5097   if (next_element != EL_UNDEFINED)
5098     Feld[oldx][oldy] = next_element;
5099
5100   TEST_DrawLevelField(oldx, oldy);
5101   TEST_DrawLevelField(newx, newy);
5102 }
5103
5104 void DrawDynamite(int x, int y)
5105 {
5106   int sx = SCREENX(x), sy = SCREENY(y);
5107   int graphic = el2img(Feld[x][y]);
5108   int frame;
5109
5110   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5111     return;
5112
5113   if (IS_WALKABLE_INSIDE(Back[x][y]))
5114     return;
5115
5116   if (Back[x][y])
5117     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5118   else if (Store[x][y])
5119     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5120
5121   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5122
5123   if (Back[x][y] || Store[x][y])
5124     DrawGraphicThruMask(sx, sy, graphic, frame);
5125   else
5126     DrawGraphic(sx, sy, graphic, frame);
5127 }
5128
5129 static void CheckDynamite(int x, int y)
5130 {
5131   if (MovDelay[x][y] != 0)      // dynamite is still waiting to explode
5132   {
5133     MovDelay[x][y]--;
5134
5135     if (MovDelay[x][y] != 0)
5136     {
5137       DrawDynamite(x, y);
5138       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5139
5140       return;
5141     }
5142   }
5143
5144   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5145
5146   Bang(x, y);
5147 }
5148
5149 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5150 {
5151   boolean num_checked_players = 0;
5152   int i;
5153
5154   for (i = 0; i < MAX_PLAYERS; i++)
5155   {
5156     if (stored_player[i].active)
5157     {
5158       int sx = stored_player[i].jx;
5159       int sy = stored_player[i].jy;
5160
5161       if (num_checked_players == 0)
5162       {
5163         *sx1 = *sx2 = sx;
5164         *sy1 = *sy2 = sy;
5165       }
5166       else
5167       {
5168         *sx1 = MIN(*sx1, sx);
5169         *sy1 = MIN(*sy1, sy);
5170         *sx2 = MAX(*sx2, sx);
5171         *sy2 = MAX(*sy2, sy);
5172       }
5173
5174       num_checked_players++;
5175     }
5176   }
5177 }
5178
5179 static boolean checkIfAllPlayersFitToScreen_RND(void)
5180 {
5181   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5182
5183   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5184
5185   return (sx2 - sx1 < SCR_FIELDX &&
5186           sy2 - sy1 < SCR_FIELDY);
5187 }
5188
5189 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5190 {
5191   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5192
5193   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5194
5195   *sx = (sx1 + sx2) / 2;
5196   *sy = (sy1 + sy2) / 2;
5197 }
5198
5199 static void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5200                                boolean center_screen, boolean quick_relocation)
5201 {
5202   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5203   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5204   boolean no_delay = (tape.warp_forward);
5205   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5206   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5207   int new_scroll_x, new_scroll_y;
5208
5209   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5210   {
5211     // case 1: quick relocation inside visible screen (without scrolling)
5212
5213     RedrawPlayfield();
5214
5215     return;
5216   }
5217
5218   if (!level.shifted_relocation || center_screen)
5219   {
5220     // relocation _with_ centering of screen
5221
5222     new_scroll_x = SCROLL_POSITION_X(x);
5223     new_scroll_y = SCROLL_POSITION_Y(y);
5224   }
5225   else
5226   {
5227     // relocation _without_ centering of screen
5228
5229     int center_scroll_x = SCROLL_POSITION_X(old_x);
5230     int center_scroll_y = SCROLL_POSITION_Y(old_y);
5231     int offset_x = x + (scroll_x - center_scroll_x);
5232     int offset_y = y + (scroll_y - center_scroll_y);
5233
5234     // for new screen position, apply previous offset to center position
5235     new_scroll_x = SCROLL_POSITION_X(offset_x);
5236     new_scroll_y = SCROLL_POSITION_Y(offset_y);
5237   }
5238
5239   if (quick_relocation)
5240   {
5241     // case 2: quick relocation (redraw without visible scrolling)
5242
5243     scroll_x = new_scroll_x;
5244     scroll_y = new_scroll_y;
5245
5246     RedrawPlayfield();
5247
5248     return;
5249   }
5250
5251   // case 3: visible relocation (with scrolling to new position)
5252
5253   ScrollScreen(NULL, SCROLL_GO_ON);     // scroll last frame to full tile
5254
5255   SetVideoFrameDelay(wait_delay_value);
5256
5257   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5258   {
5259     int dx = 0, dy = 0;
5260     int fx = FX, fy = FY;
5261
5262     dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5263     dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5264
5265     if (dx == 0 && dy == 0)             // no scrolling needed at all
5266       break;
5267
5268     scroll_x -= dx;
5269     scroll_y -= dy;
5270
5271     fx += dx * TILEX / 2;
5272     fy += dy * TILEY / 2;
5273
5274     ScrollLevel(dx, dy);
5275     DrawAllPlayers();
5276
5277     // scroll in two steps of half tile size to make things smoother
5278     BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5279
5280     // scroll second step to align at full tile size
5281     BlitScreenToBitmap(window);
5282   }
5283
5284   DrawAllPlayers();
5285   BackToFront();
5286
5287   SetVideoFrameDelay(frame_delay_value_old);
5288 }
5289
5290 static void RelocatePlayer(int jx, int jy, int el_player_raw)
5291 {
5292   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5293   int player_nr = GET_PLAYER_NR(el_player);
5294   struct PlayerInfo *player = &stored_player[player_nr];
5295   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5296   boolean no_delay = (tape.warp_forward);
5297   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5298   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5299   int old_jx = player->jx;
5300   int old_jy = player->jy;
5301   int old_element = Feld[old_jx][old_jy];
5302   int element = Feld[jx][jy];
5303   boolean player_relocated = (old_jx != jx || old_jy != jy);
5304
5305   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5306   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5307   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5308   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5309   int leave_side_horiz = move_dir_horiz;
5310   int leave_side_vert  = move_dir_vert;
5311   int enter_side = enter_side_horiz | enter_side_vert;
5312   int leave_side = leave_side_horiz | leave_side_vert;
5313
5314   if (player->buried)           // do not reanimate dead player
5315     return;
5316
5317   if (!player_relocated)        // no need to relocate the player
5318     return;
5319
5320   if (IS_PLAYER(jx, jy))        // player already placed at new position
5321   {
5322     RemoveField(jx, jy);        // temporarily remove newly placed player
5323     DrawLevelField(jx, jy);
5324   }
5325
5326   if (player->present)
5327   {
5328     while (player->MovPos)
5329     {
5330       ScrollPlayer(player, SCROLL_GO_ON);
5331       ScrollScreen(NULL, SCROLL_GO_ON);
5332
5333       AdvanceFrameAndPlayerCounters(player->index_nr);
5334
5335       DrawPlayer(player);
5336
5337       BackToFront_WithFrameDelay(wait_delay_value);
5338     }
5339
5340     DrawPlayer(player);         // needed here only to cleanup last field
5341     DrawLevelField(player->jx, player->jy);     // remove player graphic
5342
5343     player->is_moving = FALSE;
5344   }
5345
5346   if (IS_CUSTOM_ELEMENT(old_element))
5347     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5348                                CE_LEFT_BY_PLAYER,
5349                                player->index_bit, leave_side);
5350
5351   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5352                                       CE_PLAYER_LEAVES_X,
5353                                       player->index_bit, leave_side);
5354
5355   Feld[jx][jy] = el_player;
5356   InitPlayerField(jx, jy, el_player, TRUE);
5357
5358   /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5359      possible that the relocation target field did not contain a player element,
5360      but a walkable element, to which the new player was relocated -- in this
5361      case, restore that (already initialized!) element on the player field */
5362   if (!ELEM_IS_PLAYER(element)) // player may be set on walkable element
5363   {
5364     Feld[jx][jy] = element;     // restore previously existing element
5365   }
5366
5367   // only visually relocate centered player
5368   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5369                      FALSE, level.instant_relocation);
5370
5371   TestIfPlayerTouchesBadThing(jx, jy);
5372   TestIfPlayerTouchesCustomElement(jx, jy);
5373
5374   if (IS_CUSTOM_ELEMENT(element))
5375     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5376                                player->index_bit, enter_side);
5377
5378   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5379                                       player->index_bit, enter_side);
5380
5381   if (player->is_switching)
5382   {
5383     /* ensure that relocation while still switching an element does not cause
5384        a new element to be treated as also switched directly after relocation
5385        (this is important for teleporter switches that teleport the player to
5386        a place where another teleporter switch is in the same direction, which
5387        would then incorrectly be treated as immediately switched before the
5388        direction key that caused the switch was released) */
5389
5390     player->switch_x += jx - old_jx;
5391     player->switch_y += jy - old_jy;
5392   }
5393 }
5394
5395 static void Explode(int ex, int ey, int phase, int mode)
5396 {
5397   int x, y;
5398   int last_phase;
5399   int border_element;
5400
5401   // !!! eliminate this variable !!!
5402   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5403
5404   if (game.explosions_delayed)
5405   {
5406     ExplodeField[ex][ey] = mode;
5407     return;
5408   }
5409
5410   if (phase == EX_PHASE_START)          // initialize 'Store[][]' field
5411   {
5412     int center_element = Feld[ex][ey];
5413     int artwork_element, explosion_element;     // set these values later
5414
5415     // remove things displayed in background while burning dynamite
5416     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5417       Back[ex][ey] = 0;
5418
5419     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5420     {
5421       // put moving element to center field (and let it explode there)
5422       center_element = MovingOrBlocked2Element(ex, ey);
5423       RemoveMovingField(ex, ey);
5424       Feld[ex][ey] = center_element;
5425     }
5426
5427     // now "center_element" is finally determined -- set related values now
5428     artwork_element = center_element;           // for custom player artwork
5429     explosion_element = center_element;         // for custom player artwork
5430
5431     if (IS_PLAYER(ex, ey))
5432     {
5433       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5434
5435       artwork_element = stored_player[player_nr].artwork_element;
5436
5437       if (level.use_explosion_element[player_nr])
5438       {
5439         explosion_element = level.explosion_element[player_nr];
5440         artwork_element = explosion_element;
5441       }
5442     }
5443
5444     if (mode == EX_TYPE_NORMAL ||
5445         mode == EX_TYPE_CENTER ||
5446         mode == EX_TYPE_CROSS)
5447       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5448
5449     last_phase = element_info[explosion_element].explosion_delay + 1;
5450
5451     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5452     {
5453       int xx = x - ex + 1;
5454       int yy = y - ey + 1;
5455       int element;
5456
5457       if (!IN_LEV_FIELD(x, y) ||
5458           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5459           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5460         continue;
5461
5462       element = Feld[x][y];
5463
5464       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5465       {
5466         element = MovingOrBlocked2Element(x, y);
5467
5468         if (!IS_EXPLOSION_PROOF(element))
5469           RemoveMovingField(x, y);
5470       }
5471
5472       // indestructible elements can only explode in center (but not flames)
5473       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5474                                            mode == EX_TYPE_BORDER)) ||
5475           element == EL_FLAMES)
5476         continue;
5477
5478       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5479          behaviour, for example when touching a yamyam that explodes to rocks
5480          with active deadly shield, a rock is created under the player !!! */
5481       // (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8)
5482 #if 0
5483       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5484           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5485            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5486 #else
5487       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5488 #endif
5489       {
5490         if (IS_ACTIVE_BOMB(element))
5491         {
5492           // re-activate things under the bomb like gate or penguin
5493           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5494           Back[x][y] = 0;
5495         }
5496
5497         continue;
5498       }
5499
5500       // save walkable background elements while explosion on same tile
5501       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5502           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5503         Back[x][y] = element;
5504
5505       // ignite explodable elements reached by other explosion
5506       if (element == EL_EXPLOSION)
5507         element = Store2[x][y];
5508
5509       if (AmoebaNr[x][y] &&
5510           (element == EL_AMOEBA_FULL ||
5511            element == EL_BD_AMOEBA ||
5512            element == EL_AMOEBA_GROWING))
5513       {
5514         AmoebaCnt[AmoebaNr[x][y]]--;
5515         AmoebaCnt2[AmoebaNr[x][y]]--;
5516       }
5517
5518       RemoveField(x, y);
5519
5520       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5521       {
5522         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5523
5524         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5525
5526         if (PLAYERINFO(ex, ey)->use_murphy)
5527           Store[x][y] = EL_EMPTY;
5528       }
5529
5530       // !!! check this case -- currently needed for rnd_rado_negundo_v,
5531       // !!! levels 015 018 019 020 021 022 023 026 027 028 !!!
5532       else if (ELEM_IS_PLAYER(center_element))
5533         Store[x][y] = EL_EMPTY;
5534       else if (center_element == EL_YAMYAM)
5535         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5536       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5537         Store[x][y] = element_info[center_element].content.e[xx][yy];
5538 #if 1
5539       // needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5540       // (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5541       // otherwise) -- FIX THIS !!!
5542       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5543         Store[x][y] = element_info[element].content.e[1][1];
5544 #else
5545       else if (!CAN_EXPLODE(element))
5546         Store[x][y] = element_info[element].content.e[1][1];
5547 #endif
5548       else
5549         Store[x][y] = EL_EMPTY;
5550
5551       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5552           center_element == EL_AMOEBA_TO_DIAMOND)
5553         Store2[x][y] = element;
5554
5555       Feld[x][y] = EL_EXPLOSION;
5556       GfxElement[x][y] = artwork_element;
5557
5558       ExplodePhase[x][y] = 1;
5559       ExplodeDelay[x][y] = last_phase;
5560
5561       Stop[x][y] = TRUE;
5562     }
5563
5564     if (center_element == EL_YAMYAM)
5565       game.yamyam_content_nr =
5566         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5567
5568     return;
5569   }
5570
5571   if (Stop[ex][ey])
5572     return;
5573
5574   x = ex;
5575   y = ey;
5576
5577   if (phase == 1)
5578     GfxFrame[x][y] = 0;         // restart explosion animation
5579
5580   last_phase = ExplodeDelay[x][y];
5581
5582   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5583
5584   // this can happen if the player leaves an explosion just in time
5585   if (GfxElement[x][y] == EL_UNDEFINED)
5586     GfxElement[x][y] = EL_EMPTY;
5587
5588   border_element = Store2[x][y];
5589   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5590     border_element = StorePlayer[x][y];
5591
5592   if (phase == element_info[border_element].ignition_delay ||
5593       phase == last_phase)
5594   {
5595     boolean border_explosion = FALSE;
5596
5597     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5598         !PLAYER_EXPLOSION_PROTECTED(x, y))
5599     {
5600       KillPlayerUnlessExplosionProtected(x, y);
5601       border_explosion = TRUE;
5602     }
5603     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5604     {
5605       Feld[x][y] = Store2[x][y];
5606       Store2[x][y] = 0;
5607       Bang(x, y);
5608       border_explosion = TRUE;
5609     }
5610     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5611     {
5612       AmoebeUmwandeln(x, y);
5613       Store2[x][y] = 0;
5614       border_explosion = TRUE;
5615     }
5616
5617     // if an element just explodes due to another explosion (chain-reaction),
5618     // do not immediately end the new explosion when it was the last frame of
5619     // the explosion (as it would be done in the following "if"-statement!)
5620     if (border_explosion && phase == last_phase)
5621       return;
5622   }
5623
5624   if (phase == last_phase)
5625   {
5626     int element;
5627
5628     element = Feld[x][y] = Store[x][y];
5629     Store[x][y] = Store2[x][y] = 0;
5630     GfxElement[x][y] = EL_UNDEFINED;
5631
5632     // player can escape from explosions and might therefore be still alive
5633     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5634         element <= EL_PLAYER_IS_EXPLODING_4)
5635     {
5636       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5637       int explosion_element = EL_PLAYER_1 + player_nr;
5638       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5639       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5640
5641       if (level.use_explosion_element[player_nr])
5642         explosion_element = level.explosion_element[player_nr];
5643
5644       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5645                     element_info[explosion_element].content.e[xx][yy]);
5646     }
5647
5648     // restore probably existing indestructible background element
5649     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5650       element = Feld[x][y] = Back[x][y];
5651     Back[x][y] = 0;
5652
5653     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5654     GfxDir[x][y] = MV_NONE;
5655     ChangeDelay[x][y] = 0;
5656     ChangePage[x][y] = -1;
5657
5658     CustomValue[x][y] = 0;
5659
5660     InitField_WithBug2(x, y, FALSE);
5661
5662     TEST_DrawLevelField(x, y);
5663
5664     TestIfElementTouchesCustomElement(x, y);
5665
5666     if (GFX_CRUMBLED(element))
5667       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5668
5669     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5670       StorePlayer[x][y] = 0;
5671
5672     if (ELEM_IS_PLAYER(element))
5673       RelocatePlayer(x, y, element);
5674   }
5675   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5676   {
5677     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5678     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5679
5680     if (phase == delay)
5681       TEST_DrawLevelFieldCrumbled(x, y);
5682
5683     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5684     {
5685       DrawLevelElement(x, y, Back[x][y]);
5686       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5687     }
5688     else if (IS_WALKABLE_UNDER(Back[x][y]))
5689     {
5690       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5691       DrawLevelElementThruMask(x, y, Back[x][y]);
5692     }
5693     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5694       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5695   }
5696 }
5697
5698 static void DynaExplode(int ex, int ey)
5699 {
5700   int i, j;
5701   int dynabomb_element = Feld[ex][ey];
5702   int dynabomb_size = 1;
5703   boolean dynabomb_xl = FALSE;
5704   struct PlayerInfo *player;
5705   static int xy[4][2] =
5706   {
5707     { 0, -1 },
5708     { -1, 0 },
5709     { +1, 0 },
5710     { 0, +1 }
5711   };
5712
5713   if (IS_ACTIVE_BOMB(dynabomb_element))
5714   {
5715     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5716     dynabomb_size = player->dynabomb_size;
5717     dynabomb_xl = player->dynabomb_xl;
5718     player->dynabombs_left++;
5719   }
5720
5721   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5722
5723   for (i = 0; i < NUM_DIRECTIONS; i++)
5724   {
5725     for (j = 1; j <= dynabomb_size; j++)
5726     {
5727       int x = ex + j * xy[i][0];
5728       int y = ey + j * xy[i][1];
5729       int element;
5730
5731       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5732         break;
5733
5734       element = Feld[x][y];
5735
5736       // do not restart explosions of fields with active bombs
5737       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5738         continue;
5739
5740       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5741
5742       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5743           !IS_DIGGABLE(element) && !dynabomb_xl)
5744         break;
5745     }
5746   }
5747 }
5748
5749 void Bang(int x, int y)
5750 {
5751   int element = MovingOrBlocked2Element(x, y);
5752   int explosion_type = EX_TYPE_NORMAL;
5753
5754   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5755   {
5756     struct PlayerInfo *player = PLAYERINFO(x, y);
5757
5758     element = Feld[x][y] = player->initial_element;
5759
5760     if (level.use_explosion_element[player->index_nr])
5761     {
5762       int explosion_element = level.explosion_element[player->index_nr];
5763
5764       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5765         explosion_type = EX_TYPE_CROSS;
5766       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5767         explosion_type = EX_TYPE_CENTER;
5768     }
5769   }
5770
5771   switch (element)
5772   {
5773     case EL_BUG:
5774     case EL_SPACESHIP:
5775     case EL_BD_BUTTERFLY:
5776     case EL_BD_FIREFLY:
5777     case EL_YAMYAM:
5778     case EL_DARK_YAMYAM:
5779     case EL_ROBOT:
5780     case EL_PACMAN:
5781     case EL_MOLE:
5782       RaiseScoreElement(element);
5783       break;
5784
5785     case EL_DYNABOMB_PLAYER_1_ACTIVE:
5786     case EL_DYNABOMB_PLAYER_2_ACTIVE:
5787     case EL_DYNABOMB_PLAYER_3_ACTIVE:
5788     case EL_DYNABOMB_PLAYER_4_ACTIVE:
5789     case EL_DYNABOMB_INCREASE_NUMBER:
5790     case EL_DYNABOMB_INCREASE_SIZE:
5791     case EL_DYNABOMB_INCREASE_POWER:
5792       explosion_type = EX_TYPE_DYNA;
5793       break;
5794
5795     case EL_DC_LANDMINE:
5796       explosion_type = EX_TYPE_CENTER;
5797       break;
5798
5799     case EL_PENGUIN:
5800     case EL_LAMP:
5801     case EL_LAMP_ACTIVE:
5802     case EL_AMOEBA_TO_DIAMOND:
5803       if (!IS_PLAYER(x, y))     // penguin and player may be at same field
5804         explosion_type = EX_TYPE_CENTER;
5805       break;
5806
5807     default:
5808       if (element_info[element].explosion_type == EXPLODES_CROSS)
5809         explosion_type = EX_TYPE_CROSS;
5810       else if (element_info[element].explosion_type == EXPLODES_1X1)
5811         explosion_type = EX_TYPE_CENTER;
5812       break;
5813   }
5814
5815   if (explosion_type == EX_TYPE_DYNA)
5816     DynaExplode(x, y);
5817   else
5818     Explode(x, y, EX_PHASE_START, explosion_type);
5819
5820   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5821 }
5822
5823 static void SplashAcid(int x, int y)
5824 {
5825   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5826       (!IN_LEV_FIELD(x - 1, y - 2) ||
5827        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5828     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5829
5830   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5831       (!IN_LEV_FIELD(x + 1, y - 2) ||
5832        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5833     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5834
5835   PlayLevelSound(x, y, SND_ACID_SPLASHING);
5836 }
5837
5838 static void InitBeltMovement(void)
5839 {
5840   static int belt_base_element[4] =
5841   {
5842     EL_CONVEYOR_BELT_1_LEFT,
5843     EL_CONVEYOR_BELT_2_LEFT,
5844     EL_CONVEYOR_BELT_3_LEFT,
5845     EL_CONVEYOR_BELT_4_LEFT
5846   };
5847   static int belt_base_active_element[4] =
5848   {
5849     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5850     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5851     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5852     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5853   };
5854
5855   int x, y, i, j;
5856
5857   // set frame order for belt animation graphic according to belt direction
5858   for (i = 0; i < NUM_BELTS; i++)
5859   {
5860     int belt_nr = i;
5861
5862     for (j = 0; j < NUM_BELT_PARTS; j++)
5863     {
5864       int element = belt_base_active_element[belt_nr] + j;
5865       int graphic_1 = el2img(element);
5866       int graphic_2 = el2panelimg(element);
5867
5868       if (game.belt_dir[i] == MV_LEFT)
5869       {
5870         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5871         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5872       }
5873       else
5874       {
5875         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5876         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5877       }
5878     }
5879   }
5880
5881   SCAN_PLAYFIELD(x, y)
5882   {
5883     int element = Feld[x][y];
5884
5885     for (i = 0; i < NUM_BELTS; i++)
5886     {
5887       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5888       {
5889         int e_belt_nr = getBeltNrFromBeltElement(element);
5890         int belt_nr = i;
5891
5892         if (e_belt_nr == belt_nr)
5893         {
5894           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5895
5896           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5897         }
5898       }
5899     }
5900   }
5901 }
5902
5903 static void ToggleBeltSwitch(int x, int y)
5904 {
5905   static int belt_base_element[4] =
5906   {
5907     EL_CONVEYOR_BELT_1_LEFT,
5908     EL_CONVEYOR_BELT_2_LEFT,
5909     EL_CONVEYOR_BELT_3_LEFT,
5910     EL_CONVEYOR_BELT_4_LEFT
5911   };
5912   static int belt_base_active_element[4] =
5913   {
5914     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5915     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5916     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5917     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5918   };
5919   static int belt_base_switch_element[4] =
5920   {
5921     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5922     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5923     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5924     EL_CONVEYOR_BELT_4_SWITCH_LEFT
5925   };
5926   static int belt_move_dir[4] =
5927   {
5928     MV_LEFT,
5929     MV_NONE,
5930     MV_RIGHT,
5931     MV_NONE,
5932   };
5933
5934   int element = Feld[x][y];
5935   int belt_nr = getBeltNrFromBeltSwitchElement(element);
5936   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5937   int belt_dir = belt_move_dir[belt_dir_nr];
5938   int xx, yy, i;
5939
5940   if (!IS_BELT_SWITCH(element))
5941     return;
5942
5943   game.belt_dir_nr[belt_nr] = belt_dir_nr;
5944   game.belt_dir[belt_nr] = belt_dir;
5945
5946   if (belt_dir_nr == 3)
5947     belt_dir_nr = 1;
5948
5949   // set frame order for belt animation graphic according to belt direction
5950   for (i = 0; i < NUM_BELT_PARTS; i++)
5951   {
5952     int element = belt_base_active_element[belt_nr] + i;
5953     int graphic_1 = el2img(element);
5954     int graphic_2 = el2panelimg(element);
5955
5956     if (belt_dir == MV_LEFT)
5957     {
5958       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5959       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5960     }
5961     else
5962     {
5963       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5964       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5965     }
5966   }
5967
5968   SCAN_PLAYFIELD(xx, yy)
5969   {
5970     int element = Feld[xx][yy];
5971
5972     if (IS_BELT_SWITCH(element))
5973     {
5974       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5975
5976       if (e_belt_nr == belt_nr)
5977       {
5978         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5979         TEST_DrawLevelField(xx, yy);
5980       }
5981     }
5982     else if (IS_BELT(element) && belt_dir != MV_NONE)
5983     {
5984       int e_belt_nr = getBeltNrFromBeltElement(element);
5985
5986       if (e_belt_nr == belt_nr)
5987       {
5988         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
5989
5990         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
5991         TEST_DrawLevelField(xx, yy);
5992       }
5993     }
5994     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
5995     {
5996       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
5997
5998       if (e_belt_nr == belt_nr)
5999       {
6000         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
6001
6002         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
6003         TEST_DrawLevelField(xx, yy);
6004       }
6005     }
6006   }
6007 }
6008
6009 static void ToggleSwitchgateSwitch(int x, int y)
6010 {
6011   int xx, yy;
6012
6013   game.switchgate_pos = !game.switchgate_pos;
6014
6015   SCAN_PLAYFIELD(xx, yy)
6016   {
6017     int element = Feld[xx][yy];
6018
6019     if (element == EL_SWITCHGATE_SWITCH_UP)
6020     {
6021       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6022       TEST_DrawLevelField(xx, yy);
6023     }
6024     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6025     {
6026       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6027       TEST_DrawLevelField(xx, yy);
6028     }
6029     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6030     {
6031       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6032       TEST_DrawLevelField(xx, yy);
6033     }
6034     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6035     {
6036       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6037       TEST_DrawLevelField(xx, yy);
6038     }
6039     else if (element == EL_SWITCHGATE_OPEN ||
6040              element == EL_SWITCHGATE_OPENING)
6041     {
6042       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6043
6044       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6045     }
6046     else if (element == EL_SWITCHGATE_CLOSED ||
6047              element == EL_SWITCHGATE_CLOSING)
6048     {
6049       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6050
6051       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6052     }
6053   }
6054 }
6055
6056 static int getInvisibleActiveFromInvisibleElement(int element)
6057 {
6058   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6059           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6060           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6061           element);
6062 }
6063
6064 static int getInvisibleFromInvisibleActiveElement(int element)
6065 {
6066   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6067           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6068           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6069           element);
6070 }
6071
6072 static void RedrawAllLightSwitchesAndInvisibleElements(void)
6073 {
6074   int x, y;
6075
6076   SCAN_PLAYFIELD(x, y)
6077   {
6078     int element = Feld[x][y];
6079
6080     if (element == EL_LIGHT_SWITCH &&
6081         game.light_time_left > 0)
6082     {
6083       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6084       TEST_DrawLevelField(x, y);
6085     }
6086     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6087              game.light_time_left == 0)
6088     {
6089       Feld[x][y] = EL_LIGHT_SWITCH;
6090       TEST_DrawLevelField(x, y);
6091     }
6092     else if (element == EL_EMC_DRIPPER &&
6093              game.light_time_left > 0)
6094     {
6095       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6096       TEST_DrawLevelField(x, y);
6097     }
6098     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6099              game.light_time_left == 0)
6100     {
6101       Feld[x][y] = EL_EMC_DRIPPER;
6102       TEST_DrawLevelField(x, y);
6103     }
6104     else if (element == EL_INVISIBLE_STEELWALL ||
6105              element == EL_INVISIBLE_WALL ||
6106              element == EL_INVISIBLE_SAND)
6107     {
6108       if (game.light_time_left > 0)
6109         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6110
6111       TEST_DrawLevelField(x, y);
6112
6113       // uncrumble neighbour fields, if needed
6114       if (element == EL_INVISIBLE_SAND)
6115         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6116     }
6117     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6118              element == EL_INVISIBLE_WALL_ACTIVE ||
6119              element == EL_INVISIBLE_SAND_ACTIVE)
6120     {
6121       if (game.light_time_left == 0)
6122         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6123
6124       TEST_DrawLevelField(x, y);
6125
6126       // re-crumble neighbour fields, if needed
6127       if (element == EL_INVISIBLE_SAND)
6128         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6129     }
6130   }
6131 }
6132
6133 static void RedrawAllInvisibleElementsForLenses(void)
6134 {
6135   int x, y;
6136
6137   SCAN_PLAYFIELD(x, y)
6138   {
6139     int element = Feld[x][y];
6140
6141     if (element == EL_EMC_DRIPPER &&
6142         game.lenses_time_left > 0)
6143     {
6144       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6145       TEST_DrawLevelField(x, y);
6146     }
6147     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6148              game.lenses_time_left == 0)
6149     {
6150       Feld[x][y] = EL_EMC_DRIPPER;
6151       TEST_DrawLevelField(x, y);
6152     }
6153     else if (element == EL_INVISIBLE_STEELWALL ||
6154              element == EL_INVISIBLE_WALL ||
6155              element == EL_INVISIBLE_SAND)
6156     {
6157       if (game.lenses_time_left > 0)
6158         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6159
6160       TEST_DrawLevelField(x, y);
6161
6162       // uncrumble neighbour fields, if needed
6163       if (element == EL_INVISIBLE_SAND)
6164         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6165     }
6166     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6167              element == EL_INVISIBLE_WALL_ACTIVE ||
6168              element == EL_INVISIBLE_SAND_ACTIVE)
6169     {
6170       if (game.lenses_time_left == 0)
6171         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6172
6173       TEST_DrawLevelField(x, y);
6174
6175       // re-crumble neighbour fields, if needed
6176       if (element == EL_INVISIBLE_SAND)
6177         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6178     }
6179   }
6180 }
6181
6182 static void RedrawAllInvisibleElementsForMagnifier(void)
6183 {
6184   int x, y;
6185
6186   SCAN_PLAYFIELD(x, y)
6187   {
6188     int element = Feld[x][y];
6189
6190     if (element == EL_EMC_FAKE_GRASS &&
6191         game.magnify_time_left > 0)
6192     {
6193       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6194       TEST_DrawLevelField(x, y);
6195     }
6196     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6197              game.magnify_time_left == 0)
6198     {
6199       Feld[x][y] = EL_EMC_FAKE_GRASS;
6200       TEST_DrawLevelField(x, y);
6201     }
6202     else if (IS_GATE_GRAY(element) &&
6203              game.magnify_time_left > 0)
6204     {
6205       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6206                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6207                     IS_EM_GATE_GRAY(element) ?
6208                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6209                     IS_EMC_GATE_GRAY(element) ?
6210                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6211                     IS_DC_GATE_GRAY(element) ?
6212                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6213                     element);
6214       TEST_DrawLevelField(x, y);
6215     }
6216     else if (IS_GATE_GRAY_ACTIVE(element) &&
6217              game.magnify_time_left == 0)
6218     {
6219       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6220                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6221                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6222                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6223                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6224                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6225                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6226                     EL_DC_GATE_WHITE_GRAY :
6227                     element);
6228       TEST_DrawLevelField(x, y);
6229     }
6230   }
6231 }
6232
6233 static void ToggleLightSwitch(int x, int y)
6234 {
6235   int element = Feld[x][y];
6236
6237   game.light_time_left =
6238     (element == EL_LIGHT_SWITCH ?
6239      level.time_light * FRAMES_PER_SECOND : 0);
6240
6241   RedrawAllLightSwitchesAndInvisibleElements();
6242 }
6243
6244 static void ActivateTimegateSwitch(int x, int y)
6245 {
6246   int xx, yy;
6247
6248   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6249
6250   SCAN_PLAYFIELD(xx, yy)
6251   {
6252     int element = Feld[xx][yy];
6253
6254     if (element == EL_TIMEGATE_CLOSED ||
6255         element == EL_TIMEGATE_CLOSING)
6256     {
6257       Feld[xx][yy] = EL_TIMEGATE_OPENING;
6258       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6259     }
6260
6261     /*
6262     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6263     {
6264       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6265       TEST_DrawLevelField(xx, yy);
6266     }
6267     */
6268
6269   }
6270
6271   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6272                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6273 }
6274
6275 static void Impact(int x, int y)
6276 {
6277   boolean last_line = (y == lev_fieldy - 1);
6278   boolean object_hit = FALSE;
6279   boolean impact = (last_line || object_hit);
6280   int element = Feld[x][y];
6281   int smashed = EL_STEELWALL;
6282
6283   if (!last_line)       // check if element below was hit
6284   {
6285     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6286       return;
6287
6288     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6289                                          MovDir[x][y + 1] != MV_DOWN ||
6290                                          MovPos[x][y + 1] <= TILEY / 2));
6291
6292     // do not smash moving elements that left the smashed field in time
6293     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6294         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6295       object_hit = FALSE;
6296
6297 #if USE_QUICKSAND_IMPACT_BUGFIX
6298     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6299     {
6300       RemoveMovingField(x, y + 1);
6301       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6302       Feld[x][y + 2] = EL_ROCK;
6303       TEST_DrawLevelField(x, y + 2);
6304
6305       object_hit = TRUE;
6306     }
6307
6308     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6309     {
6310       RemoveMovingField(x, y + 1);
6311       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6312       Feld[x][y + 2] = EL_ROCK;
6313       TEST_DrawLevelField(x, y + 2);
6314
6315       object_hit = TRUE;
6316     }
6317 #endif
6318
6319     if (object_hit)
6320       smashed = MovingOrBlocked2Element(x, y + 1);
6321
6322     impact = (last_line || object_hit);
6323   }
6324
6325   if (!last_line && smashed == EL_ACID) // element falls into acid
6326   {
6327     SplashAcid(x, y + 1);
6328     return;
6329   }
6330
6331   // !!! not sufficient for all cases -- see EL_PEARL below !!!
6332   // only reset graphic animation if graphic really changes after impact
6333   if (impact &&
6334       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6335   {
6336     ResetGfxAnimation(x, y);
6337     TEST_DrawLevelField(x, y);
6338   }
6339
6340   if (impact && CAN_EXPLODE_IMPACT(element))
6341   {
6342     Bang(x, y);
6343     return;
6344   }
6345   else if (impact && element == EL_PEARL &&
6346            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6347   {
6348     ResetGfxAnimation(x, y);
6349
6350     Feld[x][y] = EL_PEARL_BREAKING;
6351     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6352     return;
6353   }
6354   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6355   {
6356     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6357
6358     return;
6359   }
6360
6361   if (impact && element == EL_AMOEBA_DROP)
6362   {
6363     if (object_hit && IS_PLAYER(x, y + 1))
6364       KillPlayerUnlessEnemyProtected(x, y + 1);
6365     else if (object_hit && smashed == EL_PENGUIN)
6366       Bang(x, y + 1);
6367     else
6368     {
6369       Feld[x][y] = EL_AMOEBA_GROWING;
6370       Store[x][y] = EL_AMOEBA_WET;
6371
6372       ResetRandomAnimationValue(x, y);
6373     }
6374     return;
6375   }
6376
6377   if (object_hit)               // check which object was hit
6378   {
6379     if ((CAN_PASS_MAGIC_WALL(element) && 
6380          (smashed == EL_MAGIC_WALL ||
6381           smashed == EL_BD_MAGIC_WALL)) ||
6382         (CAN_PASS_DC_MAGIC_WALL(element) &&
6383          smashed == EL_DC_MAGIC_WALL))
6384     {
6385       int xx, yy;
6386       int activated_magic_wall =
6387         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6388          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6389          EL_DC_MAGIC_WALL_ACTIVE);
6390
6391       // activate magic wall / mill
6392       SCAN_PLAYFIELD(xx, yy)
6393       {
6394         if (Feld[xx][yy] == smashed)
6395           Feld[xx][yy] = activated_magic_wall;
6396       }
6397
6398       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6399       game.magic_wall_active = TRUE;
6400
6401       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6402                             SND_MAGIC_WALL_ACTIVATING :
6403                             smashed == EL_BD_MAGIC_WALL ?
6404                             SND_BD_MAGIC_WALL_ACTIVATING :
6405                             SND_DC_MAGIC_WALL_ACTIVATING));
6406     }
6407
6408     if (IS_PLAYER(x, y + 1))
6409     {
6410       if (CAN_SMASH_PLAYER(element))
6411       {
6412         KillPlayerUnlessEnemyProtected(x, y + 1);
6413         return;
6414       }
6415     }
6416     else if (smashed == EL_PENGUIN)
6417     {
6418       if (CAN_SMASH_PLAYER(element))
6419       {
6420         Bang(x, y + 1);
6421         return;
6422       }
6423     }
6424     else if (element == EL_BD_DIAMOND)
6425     {
6426       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6427       {
6428         Bang(x, y + 1);
6429         return;
6430       }
6431     }
6432     else if (((element == EL_SP_INFOTRON ||
6433                element == EL_SP_ZONK) &&
6434               (smashed == EL_SP_SNIKSNAK ||
6435                smashed == EL_SP_ELECTRON ||
6436                smashed == EL_SP_DISK_ORANGE)) ||
6437              (element == EL_SP_INFOTRON &&
6438               smashed == EL_SP_DISK_YELLOW))
6439     {
6440       Bang(x, y + 1);
6441       return;
6442     }
6443     else if (CAN_SMASH_EVERYTHING(element))
6444     {
6445       if (IS_CLASSIC_ENEMY(smashed) ||
6446           CAN_EXPLODE_SMASHED(smashed))
6447       {
6448         Bang(x, y + 1);
6449         return;
6450       }
6451       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6452       {
6453         if (smashed == EL_LAMP ||
6454             smashed == EL_LAMP_ACTIVE)
6455         {
6456           Bang(x, y + 1);
6457           return;
6458         }
6459         else if (smashed == EL_NUT)
6460         {
6461           Feld[x][y + 1] = EL_NUT_BREAKING;
6462           PlayLevelSound(x, y, SND_NUT_BREAKING);
6463           RaiseScoreElement(EL_NUT);
6464           return;
6465         }
6466         else if (smashed == EL_PEARL)
6467         {
6468           ResetGfxAnimation(x, y);
6469
6470           Feld[x][y + 1] = EL_PEARL_BREAKING;
6471           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6472           return;
6473         }
6474         else if (smashed == EL_DIAMOND)
6475         {
6476           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6477           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6478           return;
6479         }
6480         else if (IS_BELT_SWITCH(smashed))
6481         {
6482           ToggleBeltSwitch(x, y + 1);
6483         }
6484         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6485                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6486                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6487                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6488         {
6489           ToggleSwitchgateSwitch(x, y + 1);
6490         }
6491         else if (smashed == EL_LIGHT_SWITCH ||
6492                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6493         {
6494           ToggleLightSwitch(x, y + 1);
6495         }
6496         else
6497         {
6498           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6499
6500           CheckElementChangeBySide(x, y + 1, smashed, element,
6501                                    CE_SWITCHED, CH_SIDE_TOP);
6502           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6503                                             CH_SIDE_TOP);
6504         }
6505       }
6506       else
6507       {
6508         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6509       }
6510     }
6511   }
6512
6513   // play sound of magic wall / mill
6514   if (!last_line &&
6515       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6516        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6517        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6518   {
6519     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6520       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6521     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6522       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6523     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6524       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6525
6526     return;
6527   }
6528
6529   // play sound of object that hits the ground
6530   if (last_line || object_hit)
6531     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6532 }
6533
6534 static void TurnRoundExt(int x, int y)
6535 {
6536   static struct
6537   {
6538     int dx, dy;
6539   } move_xy[] =
6540   {
6541     {  0,  0 },
6542     { -1,  0 },
6543     { +1,  0 },
6544     {  0,  0 },
6545     {  0, -1 },
6546     {  0,  0 }, { 0, 0 }, { 0, 0 },
6547     {  0, +1 }
6548   };
6549   static struct
6550   {
6551     int left, right, back;
6552   } turn[] =
6553   {
6554     { 0,        0,              0        },
6555     { MV_DOWN,  MV_UP,          MV_RIGHT },
6556     { MV_UP,    MV_DOWN,        MV_LEFT  },
6557     { 0,        0,              0        },
6558     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6559     { 0,        0,              0        },
6560     { 0,        0,              0        },
6561     { 0,        0,              0        },
6562     { MV_RIGHT, MV_LEFT,        MV_UP    }
6563   };
6564
6565   int element = Feld[x][y];
6566   int move_pattern = element_info[element].move_pattern;
6567
6568   int old_move_dir = MovDir[x][y];
6569   int left_dir  = turn[old_move_dir].left;
6570   int right_dir = turn[old_move_dir].right;
6571   int back_dir  = turn[old_move_dir].back;
6572
6573   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6574   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6575   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6576   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6577
6578   int left_x  = x + left_dx,  left_y  = y + left_dy;
6579   int right_x = x + right_dx, right_y = y + right_dy;
6580   int move_x  = x + move_dx,  move_y  = y + move_dy;
6581
6582   int xx, yy;
6583
6584   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6585   {
6586     TestIfBadThingTouchesOtherBadThing(x, y);
6587
6588     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6589       MovDir[x][y] = right_dir;
6590     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6591       MovDir[x][y] = left_dir;
6592
6593     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6594       MovDelay[x][y] = 9;
6595     else if (element == EL_BD_BUTTERFLY)     // && MovDir[x][y] == left_dir)
6596       MovDelay[x][y] = 1;
6597   }
6598   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6599   {
6600     TestIfBadThingTouchesOtherBadThing(x, y);
6601
6602     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6603       MovDir[x][y] = left_dir;
6604     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6605       MovDir[x][y] = right_dir;
6606
6607     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6608       MovDelay[x][y] = 9;
6609     else if (element == EL_BD_FIREFLY)      // && MovDir[x][y] == right_dir)
6610       MovDelay[x][y] = 1;
6611   }
6612   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6613   {
6614     TestIfBadThingTouchesOtherBadThing(x, y);
6615
6616     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6617       MovDir[x][y] = left_dir;
6618     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6619       MovDir[x][y] = right_dir;
6620
6621     if (MovDir[x][y] != old_move_dir)
6622       MovDelay[x][y] = 9;
6623   }
6624   else if (element == EL_YAMYAM)
6625   {
6626     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6627     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6628
6629     if (can_turn_left && can_turn_right)
6630       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6631     else if (can_turn_left)
6632       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6633     else if (can_turn_right)
6634       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6635     else
6636       MovDir[x][y] = back_dir;
6637
6638     MovDelay[x][y] = 16 + 16 * RND(3);
6639   }
6640   else if (element == EL_DARK_YAMYAM)
6641   {
6642     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6643                                                          left_x, left_y);
6644     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6645                                                          right_x, right_y);
6646
6647     if (can_turn_left && can_turn_right)
6648       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6649     else if (can_turn_left)
6650       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6651     else if (can_turn_right)
6652       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6653     else
6654       MovDir[x][y] = back_dir;
6655
6656     MovDelay[x][y] = 16 + 16 * RND(3);
6657   }
6658   else if (element == EL_PACMAN)
6659   {
6660     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6661     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6662
6663     if (can_turn_left && can_turn_right)
6664       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6665     else if (can_turn_left)
6666       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6667     else if (can_turn_right)
6668       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6669     else
6670       MovDir[x][y] = back_dir;
6671
6672     MovDelay[x][y] = 6 + RND(40);
6673   }
6674   else if (element == EL_PIG)
6675   {
6676     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6677     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6678     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6679     boolean should_turn_left, should_turn_right, should_move_on;
6680     int rnd_value = 24;
6681     int rnd = RND(rnd_value);
6682
6683     should_turn_left = (can_turn_left &&
6684                         (!can_move_on ||
6685                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6686                                                    y + back_dy + left_dy)));
6687     should_turn_right = (can_turn_right &&
6688                          (!can_move_on ||
6689                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6690                                                     y + back_dy + right_dy)));
6691     should_move_on = (can_move_on &&
6692                       (!can_turn_left ||
6693                        !can_turn_right ||
6694                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6695                                                  y + move_dy + left_dy) ||
6696                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6697                                                  y + move_dy + right_dy)));
6698
6699     if (should_turn_left || should_turn_right || should_move_on)
6700     {
6701       if (should_turn_left && should_turn_right && should_move_on)
6702         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6703                         rnd < 2 * rnd_value / 3 ? right_dir :
6704                         old_move_dir);
6705       else if (should_turn_left && should_turn_right)
6706         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6707       else if (should_turn_left && should_move_on)
6708         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6709       else if (should_turn_right && should_move_on)
6710         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6711       else if (should_turn_left)
6712         MovDir[x][y] = left_dir;
6713       else if (should_turn_right)
6714         MovDir[x][y] = right_dir;
6715       else if (should_move_on)
6716         MovDir[x][y] = old_move_dir;
6717     }
6718     else if (can_move_on && rnd > rnd_value / 8)
6719       MovDir[x][y] = old_move_dir;
6720     else if (can_turn_left && can_turn_right)
6721       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6722     else if (can_turn_left && rnd > rnd_value / 8)
6723       MovDir[x][y] = left_dir;
6724     else if (can_turn_right && rnd > rnd_value/8)
6725       MovDir[x][y] = right_dir;
6726     else
6727       MovDir[x][y] = back_dir;
6728
6729     xx = x + move_xy[MovDir[x][y]].dx;
6730     yy = y + move_xy[MovDir[x][y]].dy;
6731
6732     if (!IN_LEV_FIELD(xx, yy) ||
6733         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6734       MovDir[x][y] = old_move_dir;
6735
6736     MovDelay[x][y] = 0;
6737   }
6738   else if (element == EL_DRAGON)
6739   {
6740     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6741     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6742     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6743     int rnd_value = 24;
6744     int rnd = RND(rnd_value);
6745
6746     if (can_move_on && rnd > rnd_value / 8)
6747       MovDir[x][y] = old_move_dir;
6748     else if (can_turn_left && can_turn_right)
6749       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6750     else if (can_turn_left && rnd > rnd_value / 8)
6751       MovDir[x][y] = left_dir;
6752     else if (can_turn_right && rnd > rnd_value / 8)
6753       MovDir[x][y] = right_dir;
6754     else
6755       MovDir[x][y] = back_dir;
6756
6757     xx = x + move_xy[MovDir[x][y]].dx;
6758     yy = y + move_xy[MovDir[x][y]].dy;
6759
6760     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6761       MovDir[x][y] = old_move_dir;
6762
6763     MovDelay[x][y] = 0;
6764   }
6765   else if (element == EL_MOLE)
6766   {
6767     boolean can_move_on =
6768       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6769                             IS_AMOEBOID(Feld[move_x][move_y]) ||
6770                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6771     if (!can_move_on)
6772     {
6773       boolean can_turn_left =
6774         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6775                               IS_AMOEBOID(Feld[left_x][left_y])));
6776
6777       boolean can_turn_right =
6778         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6779                               IS_AMOEBOID(Feld[right_x][right_y])));
6780
6781       if (can_turn_left && can_turn_right)
6782         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6783       else if (can_turn_left)
6784         MovDir[x][y] = left_dir;
6785       else
6786         MovDir[x][y] = right_dir;
6787     }
6788
6789     if (MovDir[x][y] != old_move_dir)
6790       MovDelay[x][y] = 9;
6791   }
6792   else if (element == EL_BALLOON)
6793   {
6794     MovDir[x][y] = game.wind_direction;
6795     MovDelay[x][y] = 0;
6796   }
6797   else if (element == EL_SPRING)
6798   {
6799     if (MovDir[x][y] & MV_HORIZONTAL)
6800     {
6801       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6802           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6803       {
6804         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6805         ResetGfxAnimation(move_x, move_y);
6806         TEST_DrawLevelField(move_x, move_y);
6807
6808         MovDir[x][y] = back_dir;
6809       }
6810       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6811                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6812         MovDir[x][y] = MV_NONE;
6813     }
6814
6815     MovDelay[x][y] = 0;
6816   }
6817   else if (element == EL_ROBOT ||
6818            element == EL_SATELLITE ||
6819            element == EL_PENGUIN ||
6820            element == EL_EMC_ANDROID)
6821   {
6822     int attr_x = -1, attr_y = -1;
6823
6824     if (game.all_players_gone)
6825     {
6826       attr_x = game.exit_x;
6827       attr_y = game.exit_y;
6828     }
6829     else
6830     {
6831       int i;
6832
6833       for (i = 0; i < MAX_PLAYERS; i++)
6834       {
6835         struct PlayerInfo *player = &stored_player[i];
6836         int jx = player->jx, jy = player->jy;
6837
6838         if (!player->active)
6839           continue;
6840
6841         if (attr_x == -1 ||
6842             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6843         {
6844           attr_x = jx;
6845           attr_y = jy;
6846         }
6847       }
6848     }
6849
6850     if (element == EL_ROBOT &&
6851         game.robot_wheel_x >= 0 &&
6852         game.robot_wheel_y >= 0 &&
6853         (Feld[game.robot_wheel_x][game.robot_wheel_y] == EL_ROBOT_WHEEL_ACTIVE ||
6854          game.engine_version < VERSION_IDENT(3,1,0,0)))
6855     {
6856       attr_x = game.robot_wheel_x;
6857       attr_y = game.robot_wheel_y;
6858     }
6859
6860     if (element == EL_PENGUIN)
6861     {
6862       int i;
6863       static int xy[4][2] =
6864       {
6865         { 0, -1 },
6866         { -1, 0 },
6867         { +1, 0 },
6868         { 0, +1 }
6869       };
6870
6871       for (i = 0; i < NUM_DIRECTIONS; i++)
6872       {
6873         int ex = x + xy[i][0];
6874         int ey = y + xy[i][1];
6875
6876         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6877                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6878                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6879                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6880         {
6881           attr_x = ex;
6882           attr_y = ey;
6883           break;
6884         }
6885       }
6886     }
6887
6888     MovDir[x][y] = MV_NONE;
6889     if (attr_x < x)
6890       MovDir[x][y] |= (game.all_players_gone ? MV_RIGHT : MV_LEFT);
6891     else if (attr_x > x)
6892       MovDir[x][y] |= (game.all_players_gone ? MV_LEFT : MV_RIGHT);
6893     if (attr_y < y)
6894       MovDir[x][y] |= (game.all_players_gone ? MV_DOWN : MV_UP);
6895     else if (attr_y > y)
6896       MovDir[x][y] |= (game.all_players_gone ? MV_UP : MV_DOWN);
6897
6898     if (element == EL_ROBOT)
6899     {
6900       int newx, newy;
6901
6902       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6903         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6904       Moving2Blocked(x, y, &newx, &newy);
6905
6906       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6907         MovDelay[x][y] = 8 + 8 * !RND(3);
6908       else
6909         MovDelay[x][y] = 16;
6910     }
6911     else if (element == EL_PENGUIN)
6912     {
6913       int newx, newy;
6914
6915       MovDelay[x][y] = 1;
6916
6917       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6918       {
6919         boolean first_horiz = RND(2);
6920         int new_move_dir = MovDir[x][y];
6921
6922         MovDir[x][y] =
6923           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6924         Moving2Blocked(x, y, &newx, &newy);
6925
6926         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6927           return;
6928
6929         MovDir[x][y] =
6930           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6931         Moving2Blocked(x, y, &newx, &newy);
6932
6933         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6934           return;
6935
6936         MovDir[x][y] = old_move_dir;
6937         return;
6938       }
6939     }
6940     else if (element == EL_SATELLITE)
6941     {
6942       int newx, newy;
6943
6944       MovDelay[x][y] = 1;
6945
6946       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6947       {
6948         boolean first_horiz = RND(2);
6949         int new_move_dir = MovDir[x][y];
6950
6951         MovDir[x][y] =
6952           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6953         Moving2Blocked(x, y, &newx, &newy);
6954
6955         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6956           return;
6957
6958         MovDir[x][y] =
6959           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6960         Moving2Blocked(x, y, &newx, &newy);
6961
6962         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6963           return;
6964
6965         MovDir[x][y] = old_move_dir;
6966         return;
6967       }
6968     }
6969     else if (element == EL_EMC_ANDROID)
6970     {
6971       static int check_pos[16] =
6972       {
6973         -1,             //  0 => (invalid)
6974         7,              //  1 => MV_LEFT
6975         3,              //  2 => MV_RIGHT
6976         -1,             //  3 => (invalid)
6977         1,              //  4 =>            MV_UP
6978         0,              //  5 => MV_LEFT  | MV_UP
6979         2,              //  6 => MV_RIGHT | MV_UP
6980         -1,             //  7 => (invalid)
6981         5,              //  8 =>            MV_DOWN
6982         6,              //  9 => MV_LEFT  | MV_DOWN
6983         4,              // 10 => MV_RIGHT | MV_DOWN
6984         -1,             // 11 => (invalid)
6985         -1,             // 12 => (invalid)
6986         -1,             // 13 => (invalid)
6987         -1,             // 14 => (invalid)
6988         -1,             // 15 => (invalid)
6989       };
6990       static struct
6991       {
6992         int dx, dy;
6993         int dir;
6994       } check_xy[8] =
6995       {
6996         { -1, -1,       MV_LEFT  | MV_UP   },
6997         {  0, -1,                  MV_UP   },
6998         { +1, -1,       MV_RIGHT | MV_UP   },
6999         { +1,  0,       MV_RIGHT           },
7000         { +1, +1,       MV_RIGHT | MV_DOWN },
7001         {  0, +1,                  MV_DOWN },
7002         { -1, +1,       MV_LEFT  | MV_DOWN },
7003         { -1,  0,       MV_LEFT            },
7004       };
7005       int start_pos, check_order;
7006       boolean can_clone = FALSE;
7007       int i;
7008
7009       // check if there is any free field around current position
7010       for (i = 0; i < 8; i++)
7011       {
7012         int newx = x + check_xy[i].dx;
7013         int newy = y + check_xy[i].dy;
7014
7015         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7016         {
7017           can_clone = TRUE;
7018
7019           break;
7020         }
7021       }
7022
7023       if (can_clone)            // randomly find an element to clone
7024       {
7025         can_clone = FALSE;
7026
7027         start_pos = check_pos[RND(8)];
7028         check_order = (RND(2) ? -1 : +1);
7029
7030         for (i = 0; i < 8; i++)
7031         {
7032           int pos_raw = start_pos + i * check_order;
7033           int pos = (pos_raw + 8) % 8;
7034           int newx = x + check_xy[pos].dx;
7035           int newy = y + check_xy[pos].dy;
7036
7037           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7038           {
7039             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7040             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7041
7042             Store[x][y] = Feld[newx][newy];
7043
7044             can_clone = TRUE;
7045
7046             break;
7047           }
7048         }
7049       }
7050
7051       if (can_clone)            // randomly find a direction to move
7052       {
7053         can_clone = FALSE;
7054
7055         start_pos = check_pos[RND(8)];
7056         check_order = (RND(2) ? -1 : +1);
7057
7058         for (i = 0; i < 8; i++)
7059         {
7060           int pos_raw = start_pos + i * check_order;
7061           int pos = (pos_raw + 8) % 8;
7062           int newx = x + check_xy[pos].dx;
7063           int newy = y + check_xy[pos].dy;
7064           int new_move_dir = check_xy[pos].dir;
7065
7066           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7067           {
7068             MovDir[x][y] = new_move_dir;
7069             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7070
7071             can_clone = TRUE;
7072
7073             break;
7074           }
7075         }
7076       }
7077
7078       if (can_clone)            // cloning and moving successful
7079         return;
7080
7081       // cannot clone -- try to move towards player
7082
7083       start_pos = check_pos[MovDir[x][y] & 0x0f];
7084       check_order = (RND(2) ? -1 : +1);
7085
7086       for (i = 0; i < 3; i++)
7087       {
7088         // first check start_pos, then previous/next or (next/previous) pos
7089         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7090         int pos = (pos_raw + 8) % 8;
7091         int newx = x + check_xy[pos].dx;
7092         int newy = y + check_xy[pos].dy;
7093         int new_move_dir = check_xy[pos].dir;
7094
7095         if (IS_PLAYER(newx, newy))
7096           break;
7097
7098         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7099         {
7100           MovDir[x][y] = new_move_dir;
7101           MovDelay[x][y] = level.android_move_time * 8 + 1;
7102
7103           break;
7104         }
7105       }
7106     }
7107   }
7108   else if (move_pattern == MV_TURNING_LEFT ||
7109            move_pattern == MV_TURNING_RIGHT ||
7110            move_pattern == MV_TURNING_LEFT_RIGHT ||
7111            move_pattern == MV_TURNING_RIGHT_LEFT ||
7112            move_pattern == MV_TURNING_RANDOM ||
7113            move_pattern == MV_ALL_DIRECTIONS)
7114   {
7115     boolean can_turn_left =
7116       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7117     boolean can_turn_right =
7118       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7119
7120     if (element_info[element].move_stepsize == 0)       // "not moving"
7121       return;
7122
7123     if (move_pattern == MV_TURNING_LEFT)
7124       MovDir[x][y] = left_dir;
7125     else if (move_pattern == MV_TURNING_RIGHT)
7126       MovDir[x][y] = right_dir;
7127     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7128       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7129     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7130       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7131     else if (move_pattern == MV_TURNING_RANDOM)
7132       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7133                       can_turn_right && !can_turn_left ? right_dir :
7134                       RND(2) ? left_dir : right_dir);
7135     else if (can_turn_left && can_turn_right)
7136       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7137     else if (can_turn_left)
7138       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7139     else if (can_turn_right)
7140       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7141     else
7142       MovDir[x][y] = back_dir;
7143
7144     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7145   }
7146   else if (move_pattern == MV_HORIZONTAL ||
7147            move_pattern == MV_VERTICAL)
7148   {
7149     if (move_pattern & old_move_dir)
7150       MovDir[x][y] = back_dir;
7151     else if (move_pattern == MV_HORIZONTAL)
7152       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7153     else if (move_pattern == MV_VERTICAL)
7154       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7155
7156     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7157   }
7158   else if (move_pattern & MV_ANY_DIRECTION)
7159   {
7160     MovDir[x][y] = move_pattern;
7161     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7162   }
7163   else if (move_pattern & MV_WIND_DIRECTION)
7164   {
7165     MovDir[x][y] = game.wind_direction;
7166     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7167   }
7168   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7169   {
7170     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7171       MovDir[x][y] = left_dir;
7172     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7173       MovDir[x][y] = right_dir;
7174
7175     if (MovDir[x][y] != old_move_dir)
7176       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7177   }
7178   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7179   {
7180     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7181       MovDir[x][y] = right_dir;
7182     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7183       MovDir[x][y] = left_dir;
7184
7185     if (MovDir[x][y] != old_move_dir)
7186       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7187   }
7188   else if (move_pattern == MV_TOWARDS_PLAYER ||
7189            move_pattern == MV_AWAY_FROM_PLAYER)
7190   {
7191     int attr_x = -1, attr_y = -1;
7192     int newx, newy;
7193     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7194
7195     if (game.all_players_gone)
7196     {
7197       attr_x = game.exit_x;
7198       attr_y = game.exit_y;
7199     }
7200     else
7201     {
7202       int i;
7203
7204       for (i = 0; i < MAX_PLAYERS; i++)
7205       {
7206         struct PlayerInfo *player = &stored_player[i];
7207         int jx = player->jx, jy = player->jy;
7208
7209         if (!player->active)
7210           continue;
7211
7212         if (attr_x == -1 ||
7213             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7214         {
7215           attr_x = jx;
7216           attr_y = jy;
7217         }
7218       }
7219     }
7220
7221     MovDir[x][y] = MV_NONE;
7222     if (attr_x < x)
7223       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7224     else if (attr_x > x)
7225       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7226     if (attr_y < y)
7227       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7228     else if (attr_y > y)
7229       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7230
7231     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7232
7233     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7234     {
7235       boolean first_horiz = RND(2);
7236       int new_move_dir = MovDir[x][y];
7237
7238       if (element_info[element].move_stepsize == 0)     // "not moving"
7239       {
7240         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7241         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7242
7243         return;
7244       }
7245
7246       MovDir[x][y] =
7247         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7248       Moving2Blocked(x, y, &newx, &newy);
7249
7250       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7251         return;
7252
7253       MovDir[x][y] =
7254         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7255       Moving2Blocked(x, y, &newx, &newy);
7256
7257       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7258         return;
7259
7260       MovDir[x][y] = old_move_dir;
7261     }
7262   }
7263   else if (move_pattern == MV_WHEN_PUSHED ||
7264            move_pattern == MV_WHEN_DROPPED)
7265   {
7266     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7267       MovDir[x][y] = MV_NONE;
7268
7269     MovDelay[x][y] = 0;
7270   }
7271   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7272   {
7273     static int test_xy[7][2] =
7274     {
7275       { 0, -1 },
7276       { -1, 0 },
7277       { +1, 0 },
7278       { 0, +1 },
7279       { 0, -1 },
7280       { -1, 0 },
7281       { +1, 0 },
7282     };
7283     static int test_dir[7] =
7284     {
7285       MV_UP,
7286       MV_LEFT,
7287       MV_RIGHT,
7288       MV_DOWN,
7289       MV_UP,
7290       MV_LEFT,
7291       MV_RIGHT,
7292     };
7293     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7294     int move_preference = -1000000;     // start with very low preference
7295     int new_move_dir = MV_NONE;
7296     int start_test = RND(4);
7297     int i;
7298
7299     for (i = 0; i < NUM_DIRECTIONS; i++)
7300     {
7301       int move_dir = test_dir[start_test + i];
7302       int move_dir_preference;
7303
7304       xx = x + test_xy[start_test + i][0];
7305       yy = y + test_xy[start_test + i][1];
7306
7307       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7308           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7309       {
7310         new_move_dir = move_dir;
7311
7312         break;
7313       }
7314
7315       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7316         continue;
7317
7318       move_dir_preference = -1 * RunnerVisit[xx][yy];
7319       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7320         move_dir_preference = PlayerVisit[xx][yy];
7321
7322       if (move_dir_preference > move_preference)
7323       {
7324         // prefer field that has not been visited for the longest time
7325         move_preference = move_dir_preference;
7326         new_move_dir = move_dir;
7327       }
7328       else if (move_dir_preference == move_preference &&
7329                move_dir == old_move_dir)
7330       {
7331         // prefer last direction when all directions are preferred equally
7332         move_preference = move_dir_preference;
7333         new_move_dir = move_dir;
7334       }
7335     }
7336
7337     MovDir[x][y] = new_move_dir;
7338     if (old_move_dir != new_move_dir)
7339       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7340   }
7341 }
7342
7343 static void TurnRound(int x, int y)
7344 {
7345   int direction = MovDir[x][y];
7346
7347   TurnRoundExt(x, y);
7348
7349   GfxDir[x][y] = MovDir[x][y];
7350
7351   if (direction != MovDir[x][y])
7352     GfxFrame[x][y] = 0;
7353
7354   if (MovDelay[x][y])
7355     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7356
7357   ResetGfxFrame(x, y);
7358 }
7359
7360 static boolean JustBeingPushed(int x, int y)
7361 {
7362   int i;
7363
7364   for (i = 0; i < MAX_PLAYERS; i++)
7365   {
7366     struct PlayerInfo *player = &stored_player[i];
7367
7368     if (player->active && player->is_pushing && player->MovPos)
7369     {
7370       int next_jx = player->jx + (player->jx - player->last_jx);
7371       int next_jy = player->jy + (player->jy - player->last_jy);
7372
7373       if (x == next_jx && y == next_jy)
7374         return TRUE;
7375     }
7376   }
7377
7378   return FALSE;
7379 }
7380
7381 static void StartMoving(int x, int y)
7382 {
7383   boolean started_moving = FALSE;       // some elements can fall _and_ move
7384   int element = Feld[x][y];
7385
7386   if (Stop[x][y])
7387     return;
7388
7389   if (MovDelay[x][y] == 0)
7390     GfxAction[x][y] = ACTION_DEFAULT;
7391
7392   if (CAN_FALL(element) && y < lev_fieldy - 1)
7393   {
7394     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7395         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7396       if (JustBeingPushed(x, y))
7397         return;
7398
7399     if (element == EL_QUICKSAND_FULL)
7400     {
7401       if (IS_FREE(x, y + 1))
7402       {
7403         InitMovingField(x, y, MV_DOWN);
7404         started_moving = TRUE;
7405
7406         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7407 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7408         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7409           Store[x][y] = EL_ROCK;
7410 #else
7411         Store[x][y] = EL_ROCK;
7412 #endif
7413
7414         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7415       }
7416       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7417       {
7418         if (!MovDelay[x][y])
7419         {
7420           MovDelay[x][y] = TILEY + 1;
7421
7422           ResetGfxAnimation(x, y);
7423           ResetGfxAnimation(x, y + 1);
7424         }
7425
7426         if (MovDelay[x][y])
7427         {
7428           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7429           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7430
7431           MovDelay[x][y]--;
7432           if (MovDelay[x][y])
7433             return;
7434         }
7435
7436         Feld[x][y] = EL_QUICKSAND_EMPTY;
7437         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7438         Store[x][y + 1] = Store[x][y];
7439         Store[x][y] = 0;
7440
7441         PlayLevelSoundAction(x, y, ACTION_FILLING);
7442       }
7443       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7444       {
7445         if (!MovDelay[x][y])
7446         {
7447           MovDelay[x][y] = TILEY + 1;
7448
7449           ResetGfxAnimation(x, y);
7450           ResetGfxAnimation(x, y + 1);
7451         }
7452
7453         if (MovDelay[x][y])
7454         {
7455           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7456           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7457
7458           MovDelay[x][y]--;
7459           if (MovDelay[x][y])
7460             return;
7461         }
7462
7463         Feld[x][y] = EL_QUICKSAND_EMPTY;
7464         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7465         Store[x][y + 1] = Store[x][y];
7466         Store[x][y] = 0;
7467
7468         PlayLevelSoundAction(x, y, ACTION_FILLING);
7469       }
7470     }
7471     else if (element == EL_QUICKSAND_FAST_FULL)
7472     {
7473       if (IS_FREE(x, y + 1))
7474       {
7475         InitMovingField(x, y, MV_DOWN);
7476         started_moving = TRUE;
7477
7478         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7479 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7480         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7481           Store[x][y] = EL_ROCK;
7482 #else
7483         Store[x][y] = EL_ROCK;
7484 #endif
7485
7486         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7487       }
7488       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7489       {
7490         if (!MovDelay[x][y])
7491         {
7492           MovDelay[x][y] = TILEY + 1;
7493
7494           ResetGfxAnimation(x, y);
7495           ResetGfxAnimation(x, y + 1);
7496         }
7497
7498         if (MovDelay[x][y])
7499         {
7500           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7501           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7502
7503           MovDelay[x][y]--;
7504           if (MovDelay[x][y])
7505             return;
7506         }
7507
7508         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7509         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7510         Store[x][y + 1] = Store[x][y];
7511         Store[x][y] = 0;
7512
7513         PlayLevelSoundAction(x, y, ACTION_FILLING);
7514       }
7515       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7516       {
7517         if (!MovDelay[x][y])
7518         {
7519           MovDelay[x][y] = TILEY + 1;
7520
7521           ResetGfxAnimation(x, y);
7522           ResetGfxAnimation(x, y + 1);
7523         }
7524
7525         if (MovDelay[x][y])
7526         {
7527           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7528           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7529
7530           MovDelay[x][y]--;
7531           if (MovDelay[x][y])
7532             return;
7533         }
7534
7535         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7536         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7537         Store[x][y + 1] = Store[x][y];
7538         Store[x][y] = 0;
7539
7540         PlayLevelSoundAction(x, y, ACTION_FILLING);
7541       }
7542     }
7543     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7544              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7545     {
7546       InitMovingField(x, y, MV_DOWN);
7547       started_moving = TRUE;
7548
7549       Feld[x][y] = EL_QUICKSAND_FILLING;
7550       Store[x][y] = element;
7551
7552       PlayLevelSoundAction(x, y, ACTION_FILLING);
7553     }
7554     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7555              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7556     {
7557       InitMovingField(x, y, MV_DOWN);
7558       started_moving = TRUE;
7559
7560       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7561       Store[x][y] = element;
7562
7563       PlayLevelSoundAction(x, y, ACTION_FILLING);
7564     }
7565     else if (element == EL_MAGIC_WALL_FULL)
7566     {
7567       if (IS_FREE(x, y + 1))
7568       {
7569         InitMovingField(x, y, MV_DOWN);
7570         started_moving = TRUE;
7571
7572         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7573         Store[x][y] = EL_CHANGED(Store[x][y]);
7574       }
7575       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7576       {
7577         if (!MovDelay[x][y])
7578           MovDelay[x][y] = TILEY / 4 + 1;
7579
7580         if (MovDelay[x][y])
7581         {
7582           MovDelay[x][y]--;
7583           if (MovDelay[x][y])
7584             return;
7585         }
7586
7587         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7588         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7589         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7590         Store[x][y] = 0;
7591       }
7592     }
7593     else if (element == EL_BD_MAGIC_WALL_FULL)
7594     {
7595       if (IS_FREE(x, y + 1))
7596       {
7597         InitMovingField(x, y, MV_DOWN);
7598         started_moving = TRUE;
7599
7600         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7601         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7602       }
7603       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7604       {
7605         if (!MovDelay[x][y])
7606           MovDelay[x][y] = TILEY / 4 + 1;
7607
7608         if (MovDelay[x][y])
7609         {
7610           MovDelay[x][y]--;
7611           if (MovDelay[x][y])
7612             return;
7613         }
7614
7615         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7616         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7617         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7618         Store[x][y] = 0;
7619       }
7620     }
7621     else if (element == EL_DC_MAGIC_WALL_FULL)
7622     {
7623       if (IS_FREE(x, y + 1))
7624       {
7625         InitMovingField(x, y, MV_DOWN);
7626         started_moving = TRUE;
7627
7628         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7629         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7630       }
7631       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7632       {
7633         if (!MovDelay[x][y])
7634           MovDelay[x][y] = TILEY / 4 + 1;
7635
7636         if (MovDelay[x][y])
7637         {
7638           MovDelay[x][y]--;
7639           if (MovDelay[x][y])
7640             return;
7641         }
7642
7643         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7644         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7645         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7646         Store[x][y] = 0;
7647       }
7648     }
7649     else if ((CAN_PASS_MAGIC_WALL(element) &&
7650               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7651                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7652              (CAN_PASS_DC_MAGIC_WALL(element) &&
7653               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7654
7655     {
7656       InitMovingField(x, y, MV_DOWN);
7657       started_moving = TRUE;
7658
7659       Feld[x][y] =
7660         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7661          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7662          EL_DC_MAGIC_WALL_FILLING);
7663       Store[x][y] = element;
7664     }
7665     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7666     {
7667       SplashAcid(x, y + 1);
7668
7669       InitMovingField(x, y, MV_DOWN);
7670       started_moving = TRUE;
7671
7672       Store[x][y] = EL_ACID;
7673     }
7674     else if (
7675              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7676               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7677              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7678               CAN_FALL(element) && WasJustFalling[x][y] &&
7679               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7680
7681              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7682               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7683               (Feld[x][y + 1] == EL_BLOCKED)))
7684     {
7685       /* this is needed for a special case not covered by calling "Impact()"
7686          from "ContinueMoving()": if an element moves to a tile directly below
7687          another element which was just falling on that tile (which was empty
7688          in the previous frame), the falling element above would just stop
7689          instead of smashing the element below (in previous version, the above
7690          element was just checked for "moving" instead of "falling", resulting
7691          in incorrect smashes caused by horizontal movement of the above
7692          element; also, the case of the player being the element to smash was
7693          simply not covered here... :-/ ) */
7694
7695       CheckCollision[x][y] = 0;
7696       CheckImpact[x][y] = 0;
7697
7698       Impact(x, y);
7699     }
7700     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7701     {
7702       if (MovDir[x][y] == MV_NONE)
7703       {
7704         InitMovingField(x, y, MV_DOWN);
7705         started_moving = TRUE;
7706       }
7707     }
7708     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7709     {
7710       if (WasJustFalling[x][y]) // prevent animation from being restarted
7711         MovDir[x][y] = MV_DOWN;
7712
7713       InitMovingField(x, y, MV_DOWN);
7714       started_moving = TRUE;
7715     }
7716     else if (element == EL_AMOEBA_DROP)
7717     {
7718       Feld[x][y] = EL_AMOEBA_GROWING;
7719       Store[x][y] = EL_AMOEBA_WET;
7720     }
7721     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7722               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7723              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7724              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7725     {
7726       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7727                                 (IS_FREE(x - 1, y + 1) ||
7728                                  Feld[x - 1][y + 1] == EL_ACID));
7729       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7730                                 (IS_FREE(x + 1, y + 1) ||
7731                                  Feld[x + 1][y + 1] == EL_ACID));
7732       boolean can_fall_any  = (can_fall_left || can_fall_right);
7733       boolean can_fall_both = (can_fall_left && can_fall_right);
7734       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7735
7736       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7737       {
7738         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7739           can_fall_right = FALSE;
7740         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7741           can_fall_left = FALSE;
7742         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7743           can_fall_right = FALSE;
7744         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7745           can_fall_left = FALSE;
7746
7747         can_fall_any  = (can_fall_left || can_fall_right);
7748         can_fall_both = FALSE;
7749       }
7750
7751       if (can_fall_both)
7752       {
7753         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7754           can_fall_right = FALSE;       // slip down on left side
7755         else
7756           can_fall_left = !(can_fall_right = RND(2));
7757
7758         can_fall_both = FALSE;
7759       }
7760
7761       if (can_fall_any)
7762       {
7763         // if not determined otherwise, prefer left side for slipping down
7764         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7765         started_moving = TRUE;
7766       }
7767     }
7768     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7769     {
7770       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
7771       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7772       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7773       int belt_dir = game.belt_dir[belt_nr];
7774
7775       if ((belt_dir == MV_LEFT  && left_is_free) ||
7776           (belt_dir == MV_RIGHT && right_is_free))
7777       {
7778         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7779
7780         InitMovingField(x, y, belt_dir);
7781         started_moving = TRUE;
7782
7783         Pushed[x][y] = TRUE;
7784         Pushed[nextx][y] = TRUE;
7785
7786         GfxAction[x][y] = ACTION_DEFAULT;
7787       }
7788       else
7789       {
7790         MovDir[x][y] = 0;       // if element was moving, stop it
7791       }
7792     }
7793   }
7794
7795   // not "else if" because of elements that can fall and move (EL_SPRING)
7796   if (CAN_MOVE(element) && !started_moving)
7797   {
7798     int move_pattern = element_info[element].move_pattern;
7799     int newx, newy;
7800
7801     Moving2Blocked(x, y, &newx, &newy);
7802
7803     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7804       return;
7805
7806     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7807         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7808     {
7809       WasJustMoving[x][y] = 0;
7810       CheckCollision[x][y] = 0;
7811
7812       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7813
7814       if (Feld[x][y] != element)        // element has changed
7815         return;
7816     }
7817
7818     if (!MovDelay[x][y])        // start new movement phase
7819     {
7820       // all objects that can change their move direction after each step
7821       // (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall
7822
7823       if (element != EL_YAMYAM &&
7824           element != EL_DARK_YAMYAM &&
7825           element != EL_PACMAN &&
7826           !(move_pattern & MV_ANY_DIRECTION) &&
7827           move_pattern != MV_TURNING_LEFT &&
7828           move_pattern != MV_TURNING_RIGHT &&
7829           move_pattern != MV_TURNING_LEFT_RIGHT &&
7830           move_pattern != MV_TURNING_RIGHT_LEFT &&
7831           move_pattern != MV_TURNING_RANDOM)
7832       {
7833         TurnRound(x, y);
7834
7835         if (MovDelay[x][y] && (element == EL_BUG ||
7836                                element == EL_SPACESHIP ||
7837                                element == EL_SP_SNIKSNAK ||
7838                                element == EL_SP_ELECTRON ||
7839                                element == EL_MOLE))
7840           TEST_DrawLevelField(x, y);
7841       }
7842     }
7843
7844     if (MovDelay[x][y])         // wait some time before next movement
7845     {
7846       MovDelay[x][y]--;
7847
7848       if (element == EL_ROBOT ||
7849           element == EL_YAMYAM ||
7850           element == EL_DARK_YAMYAM)
7851       {
7852         DrawLevelElementAnimationIfNeeded(x, y, element);
7853         PlayLevelSoundAction(x, y, ACTION_WAITING);
7854       }
7855       else if (element == EL_SP_ELECTRON)
7856         DrawLevelElementAnimationIfNeeded(x, y, element);
7857       else if (element == EL_DRAGON)
7858       {
7859         int i;
7860         int dir = MovDir[x][y];
7861         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7862         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
7863         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
7864                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
7865                        dir == MV_UP     ? IMG_FLAMES_1_UP :
7866                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7867         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7868
7869         GfxAction[x][y] = ACTION_ATTACKING;
7870
7871         if (IS_PLAYER(x, y))
7872           DrawPlayerField(x, y);
7873         else
7874           TEST_DrawLevelField(x, y);
7875
7876         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7877
7878         for (i = 1; i <= 3; i++)
7879         {
7880           int xx = x + i * dx;
7881           int yy = y + i * dy;
7882           int sx = SCREENX(xx);
7883           int sy = SCREENY(yy);
7884           int flame_graphic = graphic + (i - 1);
7885
7886           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7887             break;
7888
7889           if (MovDelay[x][y])
7890           {
7891             int flamed = MovingOrBlocked2Element(xx, yy);
7892
7893             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7894               Bang(xx, yy);
7895             else
7896               RemoveMovingField(xx, yy);
7897
7898             ChangeDelay[xx][yy] = 0;
7899
7900             Feld[xx][yy] = EL_FLAMES;
7901
7902             if (IN_SCR_FIELD(sx, sy))
7903             {
7904               TEST_DrawLevelFieldCrumbled(xx, yy);
7905               DrawGraphic(sx, sy, flame_graphic, frame);
7906             }
7907           }
7908           else
7909           {
7910             if (Feld[xx][yy] == EL_FLAMES)
7911               Feld[xx][yy] = EL_EMPTY;
7912             TEST_DrawLevelField(xx, yy);
7913           }
7914         }
7915       }
7916
7917       if (MovDelay[x][y])       // element still has to wait some time
7918       {
7919         PlayLevelSoundAction(x, y, ACTION_WAITING);
7920
7921         return;
7922       }
7923     }
7924
7925     // now make next step
7926
7927     Moving2Blocked(x, y, &newx, &newy); // get next screen position
7928
7929     if (DONT_COLLIDE_WITH(element) &&
7930         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7931         !PLAYER_ENEMY_PROTECTED(newx, newy))
7932     {
7933       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7934
7935       return;
7936     }
7937
7938     else if (CAN_MOVE_INTO_ACID(element) &&
7939              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7940              !IS_MV_DIAGONAL(MovDir[x][y]) &&
7941              (MovDir[x][y] == MV_DOWN ||
7942               game.engine_version >= VERSION_IDENT(3,1,0,0)))
7943     {
7944       SplashAcid(newx, newy);
7945       Store[x][y] = EL_ACID;
7946     }
7947     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7948     {
7949       if (Feld[newx][newy] == EL_EXIT_OPEN ||
7950           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7951           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7952           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7953       {
7954         RemoveField(x, y);
7955         TEST_DrawLevelField(x, y);
7956
7957         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7958         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7959           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7960
7961         game.friends_still_needed--;
7962         if (!game.friends_still_needed &&
7963             !game.GameOver &&
7964             game.all_players_gone)
7965           LevelSolved();
7966
7967         return;
7968       }
7969       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7970       {
7971         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7972           TEST_DrawLevelField(newx, newy);
7973         else
7974           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7975       }
7976       else if (!IS_FREE(newx, newy))
7977       {
7978         GfxAction[x][y] = ACTION_WAITING;
7979
7980         if (IS_PLAYER(x, y))
7981           DrawPlayerField(x, y);
7982         else
7983           TEST_DrawLevelField(x, y);
7984
7985         return;
7986       }
7987     }
7988     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7989     {
7990       if (IS_FOOD_PIG(Feld[newx][newy]))
7991       {
7992         if (IS_MOVING(newx, newy))
7993           RemoveMovingField(newx, newy);
7994         else
7995         {
7996           Feld[newx][newy] = EL_EMPTY;
7997           TEST_DrawLevelField(newx, newy);
7998         }
7999
8000         PlayLevelSound(x, y, SND_PIG_DIGGING);
8001       }
8002       else if (!IS_FREE(newx, newy))
8003       {
8004         if (IS_PLAYER(x, y))
8005           DrawPlayerField(x, y);
8006         else
8007           TEST_DrawLevelField(x, y);
8008
8009         return;
8010       }
8011     }
8012     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8013     {
8014       if (Store[x][y] != EL_EMPTY)
8015       {
8016         boolean can_clone = FALSE;
8017         int xx, yy;
8018
8019         // check if element to clone is still there
8020         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8021         {
8022           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8023           {
8024             can_clone = TRUE;
8025
8026             break;
8027           }
8028         }
8029
8030         // cannot clone or target field not free anymore -- do not clone
8031         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8032           Store[x][y] = EL_EMPTY;
8033       }
8034
8035       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8036       {
8037         if (IS_MV_DIAGONAL(MovDir[x][y]))
8038         {
8039           int diagonal_move_dir = MovDir[x][y];
8040           int stored = Store[x][y];
8041           int change_delay = 8;
8042           int graphic;
8043
8044           // android is moving diagonally
8045
8046           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8047
8048           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8049           GfxElement[x][y] = EL_EMC_ANDROID;
8050           GfxAction[x][y] = ACTION_SHRINKING;
8051           GfxDir[x][y] = diagonal_move_dir;
8052           ChangeDelay[x][y] = change_delay;
8053
8054           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8055                                    GfxDir[x][y]);
8056
8057           DrawLevelGraphicAnimation(x, y, graphic);
8058           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8059
8060           if (Feld[newx][newy] == EL_ACID)
8061           {
8062             SplashAcid(newx, newy);
8063
8064             return;
8065           }
8066
8067           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8068
8069           Store[newx][newy] = EL_EMC_ANDROID;
8070           GfxElement[newx][newy] = EL_EMC_ANDROID;
8071           GfxAction[newx][newy] = ACTION_GROWING;
8072           GfxDir[newx][newy] = diagonal_move_dir;
8073           ChangeDelay[newx][newy] = change_delay;
8074
8075           graphic = el_act_dir2img(GfxElement[newx][newy],
8076                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8077
8078           DrawLevelGraphicAnimation(newx, newy, graphic);
8079           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8080
8081           return;
8082         }
8083         else
8084         {
8085           Feld[newx][newy] = EL_EMPTY;
8086           TEST_DrawLevelField(newx, newy);
8087
8088           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8089         }
8090       }
8091       else if (!IS_FREE(newx, newy))
8092       {
8093         return;
8094       }
8095     }
8096     else if (IS_CUSTOM_ELEMENT(element) &&
8097              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8098     {
8099       if (!DigFieldByCE(newx, newy, element))
8100         return;
8101
8102       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8103       {
8104         RunnerVisit[x][y] = FrameCounter;
8105         PlayerVisit[x][y] /= 8;         // expire player visit path
8106       }
8107     }
8108     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8109     {
8110       if (!IS_FREE(newx, newy))
8111       {
8112         if (IS_PLAYER(x, y))
8113           DrawPlayerField(x, y);
8114         else
8115           TEST_DrawLevelField(x, y);
8116
8117         return;
8118       }
8119       else
8120       {
8121         boolean wanna_flame = !RND(10);
8122         int dx = newx - x, dy = newy - y;
8123         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8124         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8125         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8126                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8127         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8128                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8129
8130         if ((wanna_flame ||
8131              IS_CLASSIC_ENEMY(element1) ||
8132              IS_CLASSIC_ENEMY(element2)) &&
8133             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8134             element1 != EL_FLAMES && element2 != EL_FLAMES)
8135         {
8136           ResetGfxAnimation(x, y);
8137           GfxAction[x][y] = ACTION_ATTACKING;
8138
8139           if (IS_PLAYER(x, y))
8140             DrawPlayerField(x, y);
8141           else
8142             TEST_DrawLevelField(x, y);
8143
8144           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8145
8146           MovDelay[x][y] = 50;
8147
8148           Feld[newx][newy] = EL_FLAMES;
8149           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8150             Feld[newx1][newy1] = EL_FLAMES;
8151           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8152             Feld[newx2][newy2] = EL_FLAMES;
8153
8154           return;
8155         }
8156       }
8157     }
8158     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8159              Feld[newx][newy] == EL_DIAMOND)
8160     {
8161       if (IS_MOVING(newx, newy))
8162         RemoveMovingField(newx, newy);
8163       else
8164       {
8165         Feld[newx][newy] = EL_EMPTY;
8166         TEST_DrawLevelField(newx, newy);
8167       }
8168
8169       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8170     }
8171     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8172              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8173     {
8174       if (AmoebaNr[newx][newy])
8175       {
8176         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8177         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8178             Feld[newx][newy] == EL_BD_AMOEBA)
8179           AmoebaCnt[AmoebaNr[newx][newy]]--;
8180       }
8181
8182       if (IS_MOVING(newx, newy))
8183       {
8184         RemoveMovingField(newx, newy);
8185       }
8186       else
8187       {
8188         Feld[newx][newy] = EL_EMPTY;
8189         TEST_DrawLevelField(newx, newy);
8190       }
8191
8192       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8193     }
8194     else if ((element == EL_PACMAN || element == EL_MOLE)
8195              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8196     {
8197       if (AmoebaNr[newx][newy])
8198       {
8199         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8200         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8201             Feld[newx][newy] == EL_BD_AMOEBA)
8202           AmoebaCnt[AmoebaNr[newx][newy]]--;
8203       }
8204
8205       if (element == EL_MOLE)
8206       {
8207         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8208         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8209
8210         ResetGfxAnimation(x, y);
8211         GfxAction[x][y] = ACTION_DIGGING;
8212         TEST_DrawLevelField(x, y);
8213
8214         MovDelay[newx][newy] = 0;       // start amoeba shrinking delay
8215
8216         return;                         // wait for shrinking amoeba
8217       }
8218       else      // element == EL_PACMAN
8219       {
8220         Feld[newx][newy] = EL_EMPTY;
8221         TEST_DrawLevelField(newx, newy);
8222         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8223       }
8224     }
8225     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8226              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8227               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8228     {
8229       // wait for shrinking amoeba to completely disappear
8230       return;
8231     }
8232     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8233     {
8234       // object was running against a wall
8235
8236       TurnRound(x, y);
8237
8238       if (GFX_ELEMENT(element) != EL_SAND)     // !!! FIX THIS (crumble) !!!
8239         DrawLevelElementAnimation(x, y, element);
8240
8241       if (DONT_TOUCH(element))
8242         TestIfBadThingTouchesPlayer(x, y);
8243
8244       return;
8245     }
8246
8247     InitMovingField(x, y, MovDir[x][y]);
8248
8249     PlayLevelSoundAction(x, y, ACTION_MOVING);
8250   }
8251
8252   if (MovDir[x][y])
8253     ContinueMoving(x, y);
8254 }
8255
8256 void ContinueMoving(int x, int y)
8257 {
8258   int element = Feld[x][y];
8259   struct ElementInfo *ei = &element_info[element];
8260   int direction = MovDir[x][y];
8261   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8262   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8263   int newx = x + dx, newy = y + dy;
8264   int stored = Store[x][y];
8265   int stored_new = Store[newx][newy];
8266   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8267   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8268   boolean last_line = (newy == lev_fieldy - 1);
8269
8270   MovPos[x][y] += getElementMoveStepsize(x, y);
8271
8272   if (pushed_by_player) // special case: moving object pushed by player
8273     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8274
8275   if (ABS(MovPos[x][y]) < TILEX)
8276   {
8277     TEST_DrawLevelField(x, y);
8278
8279     return;     // element is still moving
8280   }
8281
8282   // element reached destination field
8283
8284   Feld[x][y] = EL_EMPTY;
8285   Feld[newx][newy] = element;
8286   MovPos[x][y] = 0;     // force "not moving" for "crumbled sand"
8287
8288   if (Store[x][y] == EL_ACID)   // element is moving into acid pool
8289   {
8290     element = Feld[newx][newy] = EL_ACID;
8291   }
8292   else if (element == EL_MOLE)
8293   {
8294     Feld[x][y] = EL_SAND;
8295
8296     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8297   }
8298   else if (element == EL_QUICKSAND_FILLING)
8299   {
8300     element = Feld[newx][newy] = get_next_element(element);
8301     Store[newx][newy] = Store[x][y];
8302   }
8303   else if (element == EL_QUICKSAND_EMPTYING)
8304   {
8305     Feld[x][y] = get_next_element(element);
8306     element = Feld[newx][newy] = Store[x][y];
8307   }
8308   else if (element == EL_QUICKSAND_FAST_FILLING)
8309   {
8310     element = Feld[newx][newy] = get_next_element(element);
8311     Store[newx][newy] = Store[x][y];
8312   }
8313   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8314   {
8315     Feld[x][y] = get_next_element(element);
8316     element = Feld[newx][newy] = Store[x][y];
8317   }
8318   else if (element == EL_MAGIC_WALL_FILLING)
8319   {
8320     element = Feld[newx][newy] = get_next_element(element);
8321     if (!game.magic_wall_active)
8322       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8323     Store[newx][newy] = Store[x][y];
8324   }
8325   else if (element == EL_MAGIC_WALL_EMPTYING)
8326   {
8327     Feld[x][y] = get_next_element(element);
8328     if (!game.magic_wall_active)
8329       Feld[x][y] = EL_MAGIC_WALL_DEAD;
8330     element = Feld[newx][newy] = Store[x][y];
8331
8332     InitField(newx, newy, FALSE);
8333   }
8334   else if (element == EL_BD_MAGIC_WALL_FILLING)
8335   {
8336     element = Feld[newx][newy] = get_next_element(element);
8337     if (!game.magic_wall_active)
8338       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8339     Store[newx][newy] = Store[x][y];
8340   }
8341   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8342   {
8343     Feld[x][y] = get_next_element(element);
8344     if (!game.magic_wall_active)
8345       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8346     element = Feld[newx][newy] = Store[x][y];
8347
8348     InitField(newx, newy, FALSE);
8349   }
8350   else if (element == EL_DC_MAGIC_WALL_FILLING)
8351   {
8352     element = Feld[newx][newy] = get_next_element(element);
8353     if (!game.magic_wall_active)
8354       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8355     Store[newx][newy] = Store[x][y];
8356   }
8357   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8358   {
8359     Feld[x][y] = get_next_element(element);
8360     if (!game.magic_wall_active)
8361       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8362     element = Feld[newx][newy] = Store[x][y];
8363
8364     InitField(newx, newy, FALSE);
8365   }
8366   else if (element == EL_AMOEBA_DROPPING)
8367   {
8368     Feld[x][y] = get_next_element(element);
8369     element = Feld[newx][newy] = Store[x][y];
8370   }
8371   else if (element == EL_SOKOBAN_OBJECT)
8372   {
8373     if (Back[x][y])
8374       Feld[x][y] = Back[x][y];
8375
8376     if (Back[newx][newy])
8377       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8378
8379     Back[x][y] = Back[newx][newy] = 0;
8380   }
8381
8382   Store[x][y] = EL_EMPTY;
8383   MovPos[x][y] = 0;
8384   MovDir[x][y] = 0;
8385   MovDelay[x][y] = 0;
8386
8387   MovDelay[newx][newy] = 0;
8388
8389   if (CAN_CHANGE_OR_HAS_ACTION(element))
8390   {
8391     // copy element change control values to new field
8392     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8393     ChangePage[newx][newy]  = ChangePage[x][y];
8394     ChangeCount[newx][newy] = ChangeCount[x][y];
8395     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8396   }
8397
8398   CustomValue[newx][newy] = CustomValue[x][y];
8399
8400   ChangeDelay[x][y] = 0;
8401   ChangePage[x][y] = -1;
8402   ChangeCount[x][y] = 0;
8403   ChangeEvent[x][y] = -1;
8404
8405   CustomValue[x][y] = 0;
8406
8407   // copy animation control values to new field
8408   GfxFrame[newx][newy]  = GfxFrame[x][y];
8409   GfxRandom[newx][newy] = GfxRandom[x][y];      // keep same random value
8410   GfxAction[newx][newy] = GfxAction[x][y];      // keep action one frame
8411   GfxDir[newx][newy]    = GfxDir[x][y];         // keep element direction
8412
8413   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8414
8415   // some elements can leave other elements behind after moving
8416   if (ei->move_leave_element != EL_EMPTY &&
8417       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8418       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8419   {
8420     int move_leave_element = ei->move_leave_element;
8421
8422     // this makes it possible to leave the removed element again
8423     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8424       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8425
8426     Feld[x][y] = move_leave_element;
8427
8428     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8429       MovDir[x][y] = direction;
8430
8431     InitField(x, y, FALSE);
8432
8433     if (GFX_CRUMBLED(Feld[x][y]))
8434       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8435
8436     if (ELEM_IS_PLAYER(move_leave_element))
8437       RelocatePlayer(x, y, move_leave_element);
8438   }
8439
8440   // do this after checking for left-behind element
8441   ResetGfxAnimation(x, y);      // reset animation values for old field
8442
8443   if (!CAN_MOVE(element) ||
8444       (CAN_FALL(element) && direction == MV_DOWN &&
8445        (element == EL_SPRING ||
8446         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8447         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8448     GfxDir[x][y] = MovDir[newx][newy] = 0;
8449
8450   TEST_DrawLevelField(x, y);
8451   TEST_DrawLevelField(newx, newy);
8452
8453   Stop[newx][newy] = TRUE;      // ignore this element until the next frame
8454
8455   // prevent pushed element from moving on in pushed direction
8456   if (pushed_by_player && CAN_MOVE(element) &&
8457       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8458       !(element_info[element].move_pattern & direction))
8459     TurnRound(newx, newy);
8460
8461   // prevent elements on conveyor belt from moving on in last direction
8462   if (pushed_by_conveyor && CAN_FALL(element) &&
8463       direction & MV_HORIZONTAL)
8464     MovDir[newx][newy] = 0;
8465
8466   if (!pushed_by_player)
8467   {
8468     int nextx = newx + dx, nexty = newy + dy;
8469     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8470
8471     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8472
8473     if (CAN_FALL(element) && direction == MV_DOWN)
8474       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8475
8476     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8477       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8478
8479     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8480       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8481   }
8482
8483   if (DONT_TOUCH(element))      // object may be nasty to player or others
8484   {
8485     TestIfBadThingTouchesPlayer(newx, newy);
8486     TestIfBadThingTouchesFriend(newx, newy);
8487
8488     if (!IS_CUSTOM_ELEMENT(element))
8489       TestIfBadThingTouchesOtherBadThing(newx, newy);
8490   }
8491   else if (element == EL_PENGUIN)
8492     TestIfFriendTouchesBadThing(newx, newy);
8493
8494   if (DONT_GET_HIT_BY(element))
8495   {
8496     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8497   }
8498
8499   // give the player one last chance (one more frame) to move away
8500   if (CAN_FALL(element) && direction == MV_DOWN &&
8501       (last_line || (!IS_FREE(x, newy + 1) &&
8502                      (!IS_PLAYER(x, newy + 1) ||
8503                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8504     Impact(x, newy);
8505
8506   if (pushed_by_player && !game.use_change_when_pushing_bug)
8507   {
8508     int push_side = MV_DIR_OPPOSITE(direction);
8509     struct PlayerInfo *player = PLAYERINFO(x, y);
8510
8511     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8512                                player->index_bit, push_side);
8513     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8514                                         player->index_bit, push_side);
8515   }
8516
8517   if (element == EL_EMC_ANDROID && pushed_by_player)    // make another move
8518     MovDelay[newx][newy] = 1;
8519
8520   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8521
8522   TestIfElementTouchesCustomElement(x, y);      // empty or new element
8523   TestIfElementHitsCustomElement(newx, newy, direction);
8524   TestIfPlayerTouchesCustomElement(newx, newy);
8525   TestIfElementTouchesCustomElement(newx, newy);
8526
8527   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8528       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8529     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8530                              MV_DIR_OPPOSITE(direction));
8531 }
8532
8533 int AmoebeNachbarNr(int ax, int ay)
8534 {
8535   int i;
8536   int element = Feld[ax][ay];
8537   int group_nr = 0;
8538   static int xy[4][2] =
8539   {
8540     { 0, -1 },
8541     { -1, 0 },
8542     { +1, 0 },
8543     { 0, +1 }
8544   };
8545
8546   for (i = 0; i < NUM_DIRECTIONS; i++)
8547   {
8548     int x = ax + xy[i][0];
8549     int y = ay + xy[i][1];
8550
8551     if (!IN_LEV_FIELD(x, y))
8552       continue;
8553
8554     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8555       group_nr = AmoebaNr[x][y];
8556   }
8557
8558   return group_nr;
8559 }
8560
8561 static void AmoebenVereinigen(int ax, int ay)
8562 {
8563   int i, x, y, xx, yy;
8564   int new_group_nr = AmoebaNr[ax][ay];
8565   static int xy[4][2] =
8566   {
8567     { 0, -1 },
8568     { -1, 0 },
8569     { +1, 0 },
8570     { 0, +1 }
8571   };
8572
8573   if (new_group_nr == 0)
8574     return;
8575
8576   for (i = 0; i < NUM_DIRECTIONS; i++)
8577   {
8578     x = ax + xy[i][0];
8579     y = ay + xy[i][1];
8580
8581     if (!IN_LEV_FIELD(x, y))
8582       continue;
8583
8584     if ((Feld[x][y] == EL_AMOEBA_FULL ||
8585          Feld[x][y] == EL_BD_AMOEBA ||
8586          Feld[x][y] == EL_AMOEBA_DEAD) &&
8587         AmoebaNr[x][y] != new_group_nr)
8588     {
8589       int old_group_nr = AmoebaNr[x][y];
8590
8591       if (old_group_nr == 0)
8592         return;
8593
8594       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8595       AmoebaCnt[old_group_nr] = 0;
8596       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8597       AmoebaCnt2[old_group_nr] = 0;
8598
8599       SCAN_PLAYFIELD(xx, yy)
8600       {
8601         if (AmoebaNr[xx][yy] == old_group_nr)
8602           AmoebaNr[xx][yy] = new_group_nr;
8603       }
8604     }
8605   }
8606 }
8607
8608 void AmoebeUmwandeln(int ax, int ay)
8609 {
8610   int i, x, y;
8611
8612   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8613   {
8614     int group_nr = AmoebaNr[ax][ay];
8615
8616 #ifdef DEBUG
8617     if (group_nr == 0)
8618     {
8619       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8620       printf("AmoebeUmwandeln(): This should never happen!\n");
8621       return;
8622     }
8623 #endif
8624
8625     SCAN_PLAYFIELD(x, y)
8626     {
8627       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8628       {
8629         AmoebaNr[x][y] = 0;
8630         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8631       }
8632     }
8633
8634     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8635                             SND_AMOEBA_TURNING_TO_GEM :
8636                             SND_AMOEBA_TURNING_TO_ROCK));
8637     Bang(ax, ay);
8638   }
8639   else
8640   {
8641     static int xy[4][2] =
8642     {
8643       { 0, -1 },
8644       { -1, 0 },
8645       { +1, 0 },
8646       { 0, +1 }
8647     };
8648
8649     for (i = 0; i < NUM_DIRECTIONS; i++)
8650     {
8651       x = ax + xy[i][0];
8652       y = ay + xy[i][1];
8653
8654       if (!IN_LEV_FIELD(x, y))
8655         continue;
8656
8657       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8658       {
8659         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8660                               SND_AMOEBA_TURNING_TO_GEM :
8661                               SND_AMOEBA_TURNING_TO_ROCK));
8662         Bang(x, y);
8663       }
8664     }
8665   }
8666 }
8667
8668 static void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8669 {
8670   int x, y;
8671   int group_nr = AmoebaNr[ax][ay];
8672   boolean done = FALSE;
8673
8674 #ifdef DEBUG
8675   if (group_nr == 0)
8676   {
8677     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8678     printf("AmoebeUmwandelnBD(): This should never happen!\n");
8679     return;
8680   }
8681 #endif
8682
8683   SCAN_PLAYFIELD(x, y)
8684   {
8685     if (AmoebaNr[x][y] == group_nr &&
8686         (Feld[x][y] == EL_AMOEBA_DEAD ||
8687          Feld[x][y] == EL_BD_AMOEBA ||
8688          Feld[x][y] == EL_AMOEBA_GROWING))
8689     {
8690       AmoebaNr[x][y] = 0;
8691       Feld[x][y] = new_element;
8692       InitField(x, y, FALSE);
8693       TEST_DrawLevelField(x, y);
8694       done = TRUE;
8695     }
8696   }
8697
8698   if (done)
8699     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8700                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8701                             SND_BD_AMOEBA_TURNING_TO_GEM));
8702 }
8703
8704 static void AmoebeWaechst(int x, int y)
8705 {
8706   static unsigned int sound_delay = 0;
8707   static unsigned int sound_delay_value = 0;
8708
8709   if (!MovDelay[x][y])          // start new growing cycle
8710   {
8711     MovDelay[x][y] = 7;
8712
8713     if (DelayReached(&sound_delay, sound_delay_value))
8714     {
8715       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8716       sound_delay_value = 30;
8717     }
8718   }
8719
8720   if (MovDelay[x][y])           // wait some time before growing bigger
8721   {
8722     MovDelay[x][y]--;
8723     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8724     {
8725       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8726                                            6 - MovDelay[x][y]);
8727
8728       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8729     }
8730
8731     if (!MovDelay[x][y])
8732     {
8733       Feld[x][y] = Store[x][y];
8734       Store[x][y] = 0;
8735       TEST_DrawLevelField(x, y);
8736     }
8737   }
8738 }
8739
8740 static void AmoebaDisappearing(int x, int y)
8741 {
8742   static unsigned int sound_delay = 0;
8743   static unsigned int sound_delay_value = 0;
8744
8745   if (!MovDelay[x][y])          // start new shrinking cycle
8746   {
8747     MovDelay[x][y] = 7;
8748
8749     if (DelayReached(&sound_delay, sound_delay_value))
8750       sound_delay_value = 30;
8751   }
8752
8753   if (MovDelay[x][y])           // wait some time before shrinking
8754   {
8755     MovDelay[x][y]--;
8756     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8757     {
8758       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8759                                            6 - MovDelay[x][y]);
8760
8761       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8762     }
8763
8764     if (!MovDelay[x][y])
8765     {
8766       Feld[x][y] = EL_EMPTY;
8767       TEST_DrawLevelField(x, y);
8768
8769       // don't let mole enter this field in this cycle;
8770       // (give priority to objects falling to this field from above)
8771       Stop[x][y] = TRUE;
8772     }
8773   }
8774 }
8775
8776 static void AmoebeAbleger(int ax, int ay)
8777 {
8778   int i;
8779   int element = Feld[ax][ay];
8780   int graphic = el2img(element);
8781   int newax = ax, neway = ay;
8782   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8783   static int xy[4][2] =
8784   {
8785     { 0, -1 },
8786     { -1, 0 },
8787     { +1, 0 },
8788     { 0, +1 }
8789   };
8790
8791   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8792   {
8793     Feld[ax][ay] = EL_AMOEBA_DEAD;
8794     TEST_DrawLevelField(ax, ay);
8795     return;
8796   }
8797
8798   if (IS_ANIMATED(graphic))
8799     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8800
8801   if (!MovDelay[ax][ay])        // start making new amoeba field
8802     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8803
8804   if (MovDelay[ax][ay])         // wait some time before making new amoeba
8805   {
8806     MovDelay[ax][ay]--;
8807     if (MovDelay[ax][ay])
8808       return;
8809   }
8810
8811   if (can_drop)                 // EL_AMOEBA_WET or EL_EMC_DRIPPER
8812   {
8813     int start = RND(4);
8814     int x = ax + xy[start][0];
8815     int y = ay + xy[start][1];
8816
8817     if (!IN_LEV_FIELD(x, y))
8818       return;
8819
8820     if (IS_FREE(x, y) ||
8821         CAN_GROW_INTO(Feld[x][y]) ||
8822         Feld[x][y] == EL_QUICKSAND_EMPTY ||
8823         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8824     {
8825       newax = x;
8826       neway = y;
8827     }
8828
8829     if (newax == ax && neway == ay)
8830       return;
8831   }
8832   else                          // normal or "filled" (BD style) amoeba
8833   {
8834     int start = RND(4);
8835     boolean waiting_for_player = FALSE;
8836
8837     for (i = 0; i < NUM_DIRECTIONS; i++)
8838     {
8839       int j = (start + i) % 4;
8840       int x = ax + xy[j][0];
8841       int y = ay + xy[j][1];
8842
8843       if (!IN_LEV_FIELD(x, y))
8844         continue;
8845
8846       if (IS_FREE(x, y) ||
8847           CAN_GROW_INTO(Feld[x][y]) ||
8848           Feld[x][y] == EL_QUICKSAND_EMPTY ||
8849           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8850       {
8851         newax = x;
8852         neway = y;
8853         break;
8854       }
8855       else if (IS_PLAYER(x, y))
8856         waiting_for_player = TRUE;
8857     }
8858
8859     if (newax == ax && neway == ay)             // amoeba cannot grow
8860     {
8861       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8862       {
8863         Feld[ax][ay] = EL_AMOEBA_DEAD;
8864         TEST_DrawLevelField(ax, ay);
8865         AmoebaCnt[AmoebaNr[ax][ay]]--;
8866
8867         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   // amoeba is completely dead
8868         {
8869           if (element == EL_AMOEBA_FULL)
8870             AmoebeUmwandeln(ax, ay);
8871           else if (element == EL_BD_AMOEBA)
8872             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8873         }
8874       }
8875       return;
8876     }
8877     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8878     {
8879       // amoeba gets larger by growing in some direction
8880
8881       int new_group_nr = AmoebaNr[ax][ay];
8882
8883 #ifdef DEBUG
8884   if (new_group_nr == 0)
8885   {
8886     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8887     printf("AmoebeAbleger(): This should never happen!\n");
8888     return;
8889   }
8890 #endif
8891
8892       AmoebaNr[newax][neway] = new_group_nr;
8893       AmoebaCnt[new_group_nr]++;
8894       AmoebaCnt2[new_group_nr]++;
8895
8896       // if amoeba touches other amoeba(s) after growing, unify them
8897       AmoebenVereinigen(newax, neway);
8898
8899       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8900       {
8901         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8902         return;
8903       }
8904     }
8905   }
8906
8907   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8908       (neway == lev_fieldy - 1 && newax != ax))
8909   {
8910     Feld[newax][neway] = EL_AMOEBA_GROWING;     // creation of new amoeba
8911     Store[newax][neway] = element;
8912   }
8913   else if (neway == ay || element == EL_EMC_DRIPPER)
8914   {
8915     Feld[newax][neway] = EL_AMOEBA_DROP;        // drop left/right of amoeba
8916
8917     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8918   }
8919   else
8920   {
8921     InitMovingField(ax, ay, MV_DOWN);           // drop dripping from amoeba
8922     Feld[ax][ay] = EL_AMOEBA_DROPPING;
8923     Store[ax][ay] = EL_AMOEBA_DROP;
8924     ContinueMoving(ax, ay);
8925     return;
8926   }
8927
8928   TEST_DrawLevelField(newax, neway);
8929 }
8930
8931 static void Life(int ax, int ay)
8932 {
8933   int x1, y1, x2, y2;
8934   int life_time = 40;
8935   int element = Feld[ax][ay];
8936   int graphic = el2img(element);
8937   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8938                          level.biomaze);
8939   boolean changed = FALSE;
8940
8941   if (IS_ANIMATED(graphic))
8942     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8943
8944   if (Stop[ax][ay])
8945     return;
8946
8947   if (!MovDelay[ax][ay])        // start new "game of life" cycle
8948     MovDelay[ax][ay] = life_time;
8949
8950   if (MovDelay[ax][ay])         // wait some time before next cycle
8951   {
8952     MovDelay[ax][ay]--;
8953     if (MovDelay[ax][ay])
8954       return;
8955   }
8956
8957   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8958   {
8959     int xx = ax+x1, yy = ay+y1;
8960     int old_element = Feld[xx][yy];
8961     int num_neighbours = 0;
8962
8963     if (!IN_LEV_FIELD(xx, yy))
8964       continue;
8965
8966     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8967     {
8968       int x = xx+x2, y = yy+y2;
8969
8970       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8971         continue;
8972
8973       boolean is_player_cell = (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y));
8974       boolean is_neighbour = FALSE;
8975
8976       if (level.use_life_bugs)
8977         is_neighbour =
8978           (((Feld[x][y] == element || is_player_cell) && !Stop[x][y]) ||
8979            (IS_FREE(x, y)                             &&  Stop[x][y]));
8980       else
8981         is_neighbour =
8982           (Last[x][y] == element || is_player_cell);
8983
8984       if (is_neighbour)
8985         num_neighbours++;
8986     }
8987
8988     boolean is_free = FALSE;
8989
8990     if (level.use_life_bugs)
8991       is_free = (IS_FREE(xx, yy));
8992     else
8993       is_free = (IS_FREE(xx, yy) && Last[xx][yy] == EL_EMPTY);
8994
8995     if (xx == ax && yy == ay)           // field in the middle
8996     {
8997       if (num_neighbours < life_parameter[0] ||
8998           num_neighbours > life_parameter[1])
8999       {
9000         Feld[xx][yy] = EL_EMPTY;
9001         if (Feld[xx][yy] != old_element)
9002           TEST_DrawLevelField(xx, yy);
9003         Stop[xx][yy] = TRUE;
9004         changed = TRUE;
9005       }
9006     }
9007     else if (is_free || CAN_GROW_INTO(Feld[xx][yy]))
9008     {                                   // free border field
9009       if (num_neighbours >= life_parameter[2] &&
9010           num_neighbours <= life_parameter[3])
9011       {
9012         Feld[xx][yy] = element;
9013         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9014         if (Feld[xx][yy] != old_element)
9015           TEST_DrawLevelField(xx, yy);
9016         Stop[xx][yy] = TRUE;
9017         changed = TRUE;
9018       }
9019     }
9020   }
9021
9022   if (changed)
9023     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9024                    SND_GAME_OF_LIFE_GROWING);
9025 }
9026
9027 static void InitRobotWheel(int x, int y)
9028 {
9029   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9030 }
9031
9032 static void RunRobotWheel(int x, int y)
9033 {
9034   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9035 }
9036
9037 static void StopRobotWheel(int x, int y)
9038 {
9039   if (game.robot_wheel_x == x &&
9040       game.robot_wheel_y == y)
9041   {
9042     game.robot_wheel_x = -1;
9043     game.robot_wheel_y = -1;
9044     game.robot_wheel_active = FALSE;
9045   }
9046 }
9047
9048 static void InitTimegateWheel(int x, int y)
9049 {
9050   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9051 }
9052
9053 static void RunTimegateWheel(int x, int y)
9054 {
9055   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9056 }
9057
9058 static void InitMagicBallDelay(int x, int y)
9059 {
9060   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9061 }
9062
9063 static void ActivateMagicBall(int bx, int by)
9064 {
9065   int x, y;
9066
9067   if (level.ball_random)
9068   {
9069     int pos_border = RND(8);    // select one of the eight border elements
9070     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9071     int xx = pos_content % 3;
9072     int yy = pos_content / 3;
9073
9074     x = bx - 1 + xx;
9075     y = by - 1 + yy;
9076
9077     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9078       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9079   }
9080   else
9081   {
9082     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9083     {
9084       int xx = x - bx + 1;
9085       int yy = y - by + 1;
9086
9087       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9088         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9089     }
9090   }
9091
9092   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9093 }
9094
9095 static void CheckExit(int x, int y)
9096 {
9097   if (game.gems_still_needed > 0 ||
9098       game.sokoban_fields_still_needed > 0 ||
9099       game.sokoban_objects_still_needed > 0 ||
9100       game.lights_still_needed > 0)
9101   {
9102     int element = Feld[x][y];
9103     int graphic = el2img(element);
9104
9105     if (IS_ANIMATED(graphic))
9106       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9107
9108     return;
9109   }
9110
9111   // do not re-open exit door closed after last player
9112   if (game.all_players_gone)
9113     return;
9114
9115   Feld[x][y] = EL_EXIT_OPENING;
9116
9117   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9118 }
9119
9120 static void CheckExitEM(int x, int y)
9121 {
9122   if (game.gems_still_needed > 0 ||
9123       game.sokoban_fields_still_needed > 0 ||
9124       game.sokoban_objects_still_needed > 0 ||
9125       game.lights_still_needed > 0)
9126   {
9127     int element = Feld[x][y];
9128     int graphic = el2img(element);
9129
9130     if (IS_ANIMATED(graphic))
9131       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9132
9133     return;
9134   }
9135
9136   // do not re-open exit door closed after last player
9137   if (game.all_players_gone)
9138     return;
9139
9140   Feld[x][y] = EL_EM_EXIT_OPENING;
9141
9142   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9143 }
9144
9145 static void CheckExitSteel(int x, int y)
9146 {
9147   if (game.gems_still_needed > 0 ||
9148       game.sokoban_fields_still_needed > 0 ||
9149       game.sokoban_objects_still_needed > 0 ||
9150       game.lights_still_needed > 0)
9151   {
9152     int element = Feld[x][y];
9153     int graphic = el2img(element);
9154
9155     if (IS_ANIMATED(graphic))
9156       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9157
9158     return;
9159   }
9160
9161   // do not re-open exit door closed after last player
9162   if (game.all_players_gone)
9163     return;
9164
9165   Feld[x][y] = EL_STEEL_EXIT_OPENING;
9166
9167   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9168 }
9169
9170 static void CheckExitSteelEM(int x, int y)
9171 {
9172   if (game.gems_still_needed > 0 ||
9173       game.sokoban_fields_still_needed > 0 ||
9174       game.sokoban_objects_still_needed > 0 ||
9175       game.lights_still_needed > 0)
9176   {
9177     int element = Feld[x][y];
9178     int graphic = el2img(element);
9179
9180     if (IS_ANIMATED(graphic))
9181       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9182
9183     return;
9184   }
9185
9186   // do not re-open exit door closed after last player
9187   if (game.all_players_gone)
9188     return;
9189
9190   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9191
9192   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9193 }
9194
9195 static void CheckExitSP(int x, int y)
9196 {
9197   if (game.gems_still_needed > 0)
9198   {
9199     int element = Feld[x][y];
9200     int graphic = el2img(element);
9201
9202     if (IS_ANIMATED(graphic))
9203       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9204
9205     return;
9206   }
9207
9208   // do not re-open exit door closed after last player
9209   if (game.all_players_gone)
9210     return;
9211
9212   Feld[x][y] = EL_SP_EXIT_OPENING;
9213
9214   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9215 }
9216
9217 static void CloseAllOpenTimegates(void)
9218 {
9219   int x, y;
9220
9221   SCAN_PLAYFIELD(x, y)
9222   {
9223     int element = Feld[x][y];
9224
9225     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9226     {
9227       Feld[x][y] = EL_TIMEGATE_CLOSING;
9228
9229       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9230     }
9231   }
9232 }
9233
9234 static void DrawTwinkleOnField(int x, int y)
9235 {
9236   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9237     return;
9238
9239   if (Feld[x][y] == EL_BD_DIAMOND)
9240     return;
9241
9242   if (MovDelay[x][y] == 0)      // next animation frame
9243     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9244
9245   if (MovDelay[x][y] != 0)      // wait some time before next frame
9246   {
9247     MovDelay[x][y]--;
9248
9249     DrawLevelElementAnimation(x, y, Feld[x][y]);
9250
9251     if (MovDelay[x][y] != 0)
9252     {
9253       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9254                                            10 - MovDelay[x][y]);
9255
9256       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9257     }
9258   }
9259 }
9260
9261 static void MauerWaechst(int x, int y)
9262 {
9263   int delay = 6;
9264
9265   if (!MovDelay[x][y])          // next animation frame
9266     MovDelay[x][y] = 3 * delay;
9267
9268   if (MovDelay[x][y])           // wait some time before next frame
9269   {
9270     MovDelay[x][y]--;
9271
9272     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9273     {
9274       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9275       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9276
9277       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9278     }
9279
9280     if (!MovDelay[x][y])
9281     {
9282       if (MovDir[x][y] == MV_LEFT)
9283       {
9284         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9285           TEST_DrawLevelField(x - 1, y);
9286       }
9287       else if (MovDir[x][y] == MV_RIGHT)
9288       {
9289         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9290           TEST_DrawLevelField(x + 1, y);
9291       }
9292       else if (MovDir[x][y] == MV_UP)
9293       {
9294         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9295           TEST_DrawLevelField(x, y - 1);
9296       }
9297       else
9298       {
9299         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9300           TEST_DrawLevelField(x, y + 1);
9301       }
9302
9303       Feld[x][y] = Store[x][y];
9304       Store[x][y] = 0;
9305       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9306       TEST_DrawLevelField(x, y);
9307     }
9308   }
9309 }
9310
9311 static void MauerAbleger(int ax, int ay)
9312 {
9313   int element = Feld[ax][ay];
9314   int graphic = el2img(element);
9315   boolean oben_frei = FALSE, unten_frei = FALSE;
9316   boolean links_frei = FALSE, rechts_frei = FALSE;
9317   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9318   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9319   boolean new_wall = FALSE;
9320
9321   if (IS_ANIMATED(graphic))
9322     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9323
9324   if (!MovDelay[ax][ay])        // start building new wall
9325     MovDelay[ax][ay] = 6;
9326
9327   if (MovDelay[ax][ay])         // wait some time before building new wall
9328   {
9329     MovDelay[ax][ay]--;
9330     if (MovDelay[ax][ay])
9331       return;
9332   }
9333
9334   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9335     oben_frei = TRUE;
9336   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9337     unten_frei = TRUE;
9338   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9339     links_frei = TRUE;
9340   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9341     rechts_frei = TRUE;
9342
9343   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9344       element == EL_EXPANDABLE_WALL_ANY)
9345   {
9346     if (oben_frei)
9347     {
9348       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9349       Store[ax][ay-1] = element;
9350       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9351       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9352         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9353                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9354       new_wall = TRUE;
9355     }
9356     if (unten_frei)
9357     {
9358       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9359       Store[ax][ay+1] = element;
9360       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9361       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9362         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9363                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9364       new_wall = TRUE;
9365     }
9366   }
9367
9368   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9369       element == EL_EXPANDABLE_WALL_ANY ||
9370       element == EL_EXPANDABLE_WALL ||
9371       element == EL_BD_EXPANDABLE_WALL)
9372   {
9373     if (links_frei)
9374     {
9375       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9376       Store[ax-1][ay] = element;
9377       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9378       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9379         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9380                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9381       new_wall = TRUE;
9382     }
9383
9384     if (rechts_frei)
9385     {
9386       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9387       Store[ax+1][ay] = element;
9388       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9389       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9390         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9391                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9392       new_wall = TRUE;
9393     }
9394   }
9395
9396   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9397     TEST_DrawLevelField(ax, ay);
9398
9399   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9400     oben_massiv = TRUE;
9401   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9402     unten_massiv = TRUE;
9403   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9404     links_massiv = TRUE;
9405   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9406     rechts_massiv = TRUE;
9407
9408   if (((oben_massiv && unten_massiv) ||
9409        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9410        element == EL_EXPANDABLE_WALL) &&
9411       ((links_massiv && rechts_massiv) ||
9412        element == EL_EXPANDABLE_WALL_VERTICAL))
9413     Feld[ax][ay] = EL_WALL;
9414
9415   if (new_wall)
9416     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9417 }
9418
9419 static void MauerAblegerStahl(int ax, int ay)
9420 {
9421   int element = Feld[ax][ay];
9422   int graphic = el2img(element);
9423   boolean oben_frei = FALSE, unten_frei = FALSE;
9424   boolean links_frei = FALSE, rechts_frei = FALSE;
9425   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9426   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9427   boolean new_wall = FALSE;
9428
9429   if (IS_ANIMATED(graphic))
9430     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9431
9432   if (!MovDelay[ax][ay])        // start building new wall
9433     MovDelay[ax][ay] = 6;
9434
9435   if (MovDelay[ax][ay])         // wait some time before building new wall
9436   {
9437     MovDelay[ax][ay]--;
9438     if (MovDelay[ax][ay])
9439       return;
9440   }
9441
9442   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9443     oben_frei = TRUE;
9444   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9445     unten_frei = TRUE;
9446   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9447     links_frei = TRUE;
9448   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9449     rechts_frei = TRUE;
9450
9451   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9452       element == EL_EXPANDABLE_STEELWALL_ANY)
9453   {
9454     if (oben_frei)
9455     {
9456       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9457       Store[ax][ay-1] = element;
9458       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9459       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9460         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9461                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9462       new_wall = TRUE;
9463     }
9464     if (unten_frei)
9465     {
9466       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9467       Store[ax][ay+1] = element;
9468       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9469       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9470         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9471                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9472       new_wall = TRUE;
9473     }
9474   }
9475
9476   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9477       element == EL_EXPANDABLE_STEELWALL_ANY)
9478   {
9479     if (links_frei)
9480     {
9481       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9482       Store[ax-1][ay] = element;
9483       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9484       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9485         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9486                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9487       new_wall = TRUE;
9488     }
9489
9490     if (rechts_frei)
9491     {
9492       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9493       Store[ax+1][ay] = element;
9494       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9495       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9496         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9497                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9498       new_wall = TRUE;
9499     }
9500   }
9501
9502   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9503     oben_massiv = TRUE;
9504   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9505     unten_massiv = TRUE;
9506   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9507     links_massiv = TRUE;
9508   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9509     rechts_massiv = TRUE;
9510
9511   if (((oben_massiv && unten_massiv) ||
9512        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9513       ((links_massiv && rechts_massiv) ||
9514        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9515     Feld[ax][ay] = EL_STEELWALL;
9516
9517   if (new_wall)
9518     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9519 }
9520
9521 static void CheckForDragon(int x, int y)
9522 {
9523   int i, j;
9524   boolean dragon_found = FALSE;
9525   static int xy[4][2] =
9526   {
9527     { 0, -1 },
9528     { -1, 0 },
9529     { +1, 0 },
9530     { 0, +1 }
9531   };
9532
9533   for (i = 0; i < NUM_DIRECTIONS; i++)
9534   {
9535     for (j = 0; j < 4; j++)
9536     {
9537       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9538
9539       if (IN_LEV_FIELD(xx, yy) &&
9540           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9541       {
9542         if (Feld[xx][yy] == EL_DRAGON)
9543           dragon_found = TRUE;
9544       }
9545       else
9546         break;
9547     }
9548   }
9549
9550   if (!dragon_found)
9551   {
9552     for (i = 0; i < NUM_DIRECTIONS; i++)
9553     {
9554       for (j = 0; j < 3; j++)
9555       {
9556         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9557   
9558         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9559         {
9560           Feld[xx][yy] = EL_EMPTY;
9561           TEST_DrawLevelField(xx, yy);
9562         }
9563         else
9564           break;
9565       }
9566     }
9567   }
9568 }
9569
9570 static void InitBuggyBase(int x, int y)
9571 {
9572   int element = Feld[x][y];
9573   int activating_delay = FRAMES_PER_SECOND / 4;
9574
9575   ChangeDelay[x][y] =
9576     (element == EL_SP_BUGGY_BASE ?
9577      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9578      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9579      activating_delay :
9580      element == EL_SP_BUGGY_BASE_ACTIVE ?
9581      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9582 }
9583
9584 static void WarnBuggyBase(int x, int y)
9585 {
9586   int i;
9587   static int xy[4][2] =
9588   {
9589     { 0, -1 },
9590     { -1, 0 },
9591     { +1, 0 },
9592     { 0, +1 }
9593   };
9594
9595   for (i = 0; i < NUM_DIRECTIONS; i++)
9596   {
9597     int xx = x + xy[i][0];
9598     int yy = y + xy[i][1];
9599
9600     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9601     {
9602       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9603
9604       break;
9605     }
9606   }
9607 }
9608
9609 static void InitTrap(int x, int y)
9610 {
9611   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9612 }
9613
9614 static void ActivateTrap(int x, int y)
9615 {
9616   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9617 }
9618
9619 static void ChangeActiveTrap(int x, int y)
9620 {
9621   int graphic = IMG_TRAP_ACTIVE;
9622
9623   // if new animation frame was drawn, correct crumbled sand border
9624   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9625     TEST_DrawLevelFieldCrumbled(x, y);
9626 }
9627
9628 static int getSpecialActionElement(int element, int number, int base_element)
9629 {
9630   return (element != EL_EMPTY ? element :
9631           number != -1 ? base_element + number - 1 :
9632           EL_EMPTY);
9633 }
9634
9635 static int getModifiedActionNumber(int value_old, int operator, int operand,
9636                                    int value_min, int value_max)
9637 {
9638   int value_new = (operator == CA_MODE_SET      ? operand :
9639                    operator == CA_MODE_ADD      ? value_old + operand :
9640                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9641                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9642                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9643                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9644                    value_old);
9645
9646   return (value_new < value_min ? value_min :
9647           value_new > value_max ? value_max :
9648           value_new);
9649 }
9650
9651 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9652 {
9653   struct ElementInfo *ei = &element_info[element];
9654   struct ElementChangeInfo *change = &ei->change_page[page];
9655   int target_element = change->target_element;
9656   int action_type = change->action_type;
9657   int action_mode = change->action_mode;
9658   int action_arg = change->action_arg;
9659   int action_element = change->action_element;
9660   int i;
9661
9662   if (!change->has_action)
9663     return;
9664
9665   // ---------- determine action paramater values -----------------------------
9666
9667   int level_time_value =
9668     (level.time > 0 ? TimeLeft :
9669      TimePlayed);
9670
9671   int action_arg_element_raw =
9672     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9673      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9674      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9675      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
9676      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9677      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
9678      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
9679      EL_EMPTY);
9680   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9681
9682   int action_arg_direction =
9683     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9684      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9685      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9686      change->actual_trigger_side :
9687      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9688      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9689      MV_NONE);
9690
9691   int action_arg_number_min =
9692     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9693      CA_ARG_MIN);
9694
9695   int action_arg_number_max =
9696     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9697      action_type == CA_SET_LEVEL_GEMS ? 999 :
9698      action_type == CA_SET_LEVEL_TIME ? 9999 :
9699      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9700      action_type == CA_SET_CE_VALUE ? 9999 :
9701      action_type == CA_SET_CE_SCORE ? 9999 :
9702      CA_ARG_MAX);
9703
9704   int action_arg_number_reset =
9705     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9706      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9707      action_type == CA_SET_LEVEL_TIME ? level.time :
9708      action_type == CA_SET_LEVEL_SCORE ? 0 :
9709      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9710      action_type == CA_SET_CE_SCORE ? 0 :
9711      0);
9712
9713   int action_arg_number =
9714     (action_arg <= CA_ARG_MAX ? action_arg :
9715      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9716      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9717      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9718      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9719      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9720      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9721      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9722      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9723      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9724      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9725      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? game.gems_still_needed :
9726      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? game.score :
9727      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9728      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9729      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9730      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9731      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9732      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9733      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
9734      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9735      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
9736      -1);
9737
9738   int action_arg_number_old =
9739     (action_type == CA_SET_LEVEL_GEMS ? game.gems_still_needed :
9740      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9741      action_type == CA_SET_LEVEL_SCORE ? game.score :
9742      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9743      action_type == CA_SET_CE_SCORE ? ei->collect_score :
9744      0);
9745
9746   int action_arg_number_new =
9747     getModifiedActionNumber(action_arg_number_old,
9748                             action_mode, action_arg_number,
9749                             action_arg_number_min, action_arg_number_max);
9750
9751   int trigger_player_bits =
9752     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9753      change->actual_trigger_player_bits : change->trigger_player);
9754
9755   int action_arg_player_bits =
9756     (action_arg >= CA_ARG_PLAYER_1 &&
9757      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9758      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9759      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9760      PLAYER_BITS_ANY);
9761
9762   // ---------- execute action  -----------------------------------------------
9763
9764   switch (action_type)
9765   {
9766     case CA_NO_ACTION:
9767     {
9768       return;
9769     }
9770
9771     // ---------- level actions  ----------------------------------------------
9772
9773     case CA_RESTART_LEVEL:
9774     {
9775       game.restart_level = TRUE;
9776
9777       break;
9778     }
9779
9780     case CA_SHOW_ENVELOPE:
9781     {
9782       int element = getSpecialActionElement(action_arg_element,
9783                                             action_arg_number, EL_ENVELOPE_1);
9784
9785       if (IS_ENVELOPE(element))
9786         local_player->show_envelope = element;
9787
9788       break;
9789     }
9790
9791     case CA_SET_LEVEL_TIME:
9792     {
9793       if (level.time > 0)       // only modify limited time value
9794       {
9795         TimeLeft = action_arg_number_new;
9796
9797         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9798
9799         DisplayGameControlValues();
9800
9801         if (!TimeLeft && setup.time_limit)
9802           for (i = 0; i < MAX_PLAYERS; i++)
9803             KillPlayer(&stored_player[i]);
9804       }
9805
9806       break;
9807     }
9808
9809     case CA_SET_LEVEL_SCORE:
9810     {
9811       game.score = action_arg_number_new;
9812
9813       game_panel_controls[GAME_PANEL_SCORE].value = game.score;
9814
9815       DisplayGameControlValues();
9816
9817       break;
9818     }
9819
9820     case CA_SET_LEVEL_GEMS:
9821     {
9822       game.gems_still_needed = action_arg_number_new;
9823
9824       game.snapshot.collected_item = TRUE;
9825
9826       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
9827
9828       DisplayGameControlValues();
9829
9830       break;
9831     }
9832
9833     case CA_SET_LEVEL_WIND:
9834     {
9835       game.wind_direction = action_arg_direction;
9836
9837       break;
9838     }
9839
9840     case CA_SET_LEVEL_RANDOM_SEED:
9841     {
9842       // ensure that setting a new random seed while playing is predictable
9843       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
9844
9845       break;
9846     }
9847
9848     // ---------- player actions  ---------------------------------------------
9849
9850     case CA_MOVE_PLAYER:
9851     {
9852       // automatically move to the next field in specified direction
9853       for (i = 0; i < MAX_PLAYERS; i++)
9854         if (trigger_player_bits & (1 << i))
9855           stored_player[i].programmed_action = action_arg_direction;
9856
9857       break;
9858     }
9859
9860     case CA_EXIT_PLAYER:
9861     {
9862       for (i = 0; i < MAX_PLAYERS; i++)
9863         if (action_arg_player_bits & (1 << i))
9864           ExitPlayer(&stored_player[i]);
9865
9866       if (game.players_still_needed == 0)
9867         LevelSolved();
9868
9869       break;
9870     }
9871
9872     case CA_KILL_PLAYER:
9873     {
9874       for (i = 0; i < MAX_PLAYERS; i++)
9875         if (action_arg_player_bits & (1 << i))
9876           KillPlayer(&stored_player[i]);
9877
9878       break;
9879     }
9880
9881     case CA_SET_PLAYER_KEYS:
9882     {
9883       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9884       int element = getSpecialActionElement(action_arg_element,
9885                                             action_arg_number, EL_KEY_1);
9886
9887       if (IS_KEY(element))
9888       {
9889         for (i = 0; i < MAX_PLAYERS; i++)
9890         {
9891           if (trigger_player_bits & (1 << i))
9892           {
9893             stored_player[i].key[KEY_NR(element)] = key_state;
9894
9895             DrawGameDoorValues();
9896           }
9897         }
9898       }
9899
9900       break;
9901     }
9902
9903     case CA_SET_PLAYER_SPEED:
9904     {
9905       for (i = 0; i < MAX_PLAYERS; i++)
9906       {
9907         if (trigger_player_bits & (1 << i))
9908         {
9909           int move_stepsize = TILEX / stored_player[i].move_delay_value;
9910
9911           if (action_arg == CA_ARG_SPEED_FASTER &&
9912               stored_player[i].cannot_move)
9913           {
9914             action_arg_number = STEPSIZE_VERY_SLOW;
9915           }
9916           else if (action_arg == CA_ARG_SPEED_SLOWER ||
9917                    action_arg == CA_ARG_SPEED_FASTER)
9918           {
9919             action_arg_number = 2;
9920             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9921                            CA_MODE_MULTIPLY);
9922           }
9923           else if (action_arg == CA_ARG_NUMBER_RESET)
9924           {
9925             action_arg_number = level.initial_player_stepsize[i];
9926           }
9927
9928           move_stepsize =
9929             getModifiedActionNumber(move_stepsize,
9930                                     action_mode,
9931                                     action_arg_number,
9932                                     action_arg_number_min,
9933                                     action_arg_number_max);
9934
9935           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9936         }
9937       }
9938
9939       break;
9940     }
9941
9942     case CA_SET_PLAYER_SHIELD:
9943     {
9944       for (i = 0; i < MAX_PLAYERS; i++)
9945       {
9946         if (trigger_player_bits & (1 << i))
9947         {
9948           if (action_arg == CA_ARG_SHIELD_OFF)
9949           {
9950             stored_player[i].shield_normal_time_left = 0;
9951             stored_player[i].shield_deadly_time_left = 0;
9952           }
9953           else if (action_arg == CA_ARG_SHIELD_NORMAL)
9954           {
9955             stored_player[i].shield_normal_time_left = 999999;
9956           }
9957           else if (action_arg == CA_ARG_SHIELD_DEADLY)
9958           {
9959             stored_player[i].shield_normal_time_left = 999999;
9960             stored_player[i].shield_deadly_time_left = 999999;
9961           }
9962         }
9963       }
9964
9965       break;
9966     }
9967
9968     case CA_SET_PLAYER_GRAVITY:
9969     {
9970       for (i = 0; i < MAX_PLAYERS; i++)
9971       {
9972         if (trigger_player_bits & (1 << i))
9973         {
9974           stored_player[i].gravity =
9975             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
9976              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
9977              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9978              stored_player[i].gravity);
9979         }
9980       }
9981
9982       break;
9983     }
9984
9985     case CA_SET_PLAYER_ARTWORK:
9986     {
9987       for (i = 0; i < MAX_PLAYERS; i++)
9988       {
9989         if (trigger_player_bits & (1 << i))
9990         {
9991           int artwork_element = action_arg_element;
9992
9993           if (action_arg == CA_ARG_ELEMENT_RESET)
9994             artwork_element =
9995               (level.use_artwork_element[i] ? level.artwork_element[i] :
9996                stored_player[i].element_nr);
9997
9998           if (stored_player[i].artwork_element != artwork_element)
9999             stored_player[i].Frame = 0;
10000
10001           stored_player[i].artwork_element = artwork_element;
10002
10003           SetPlayerWaiting(&stored_player[i], FALSE);
10004
10005           // set number of special actions for bored and sleeping animation
10006           stored_player[i].num_special_action_bored =
10007             get_num_special_action(artwork_element,
10008                                    ACTION_BORING_1, ACTION_BORING_LAST);
10009           stored_player[i].num_special_action_sleeping =
10010             get_num_special_action(artwork_element,
10011                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10012         }
10013       }
10014
10015       break;
10016     }
10017
10018     case CA_SET_PLAYER_INVENTORY:
10019     {
10020       for (i = 0; i < MAX_PLAYERS; i++)
10021       {
10022         struct PlayerInfo *player = &stored_player[i];
10023         int j, k;
10024
10025         if (trigger_player_bits & (1 << i))
10026         {
10027           int inventory_element = action_arg_element;
10028
10029           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10030               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10031               action_arg == CA_ARG_ELEMENT_ACTION)
10032           {
10033             int element = inventory_element;
10034             int collect_count = element_info[element].collect_count_initial;
10035
10036             if (!IS_CUSTOM_ELEMENT(element))
10037               collect_count = 1;
10038
10039             if (collect_count == 0)
10040               player->inventory_infinite_element = element;
10041             else
10042               for (k = 0; k < collect_count; k++)
10043                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10044                   player->inventory_element[player->inventory_size++] =
10045                     element;
10046           }
10047           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10048                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10049                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10050           {
10051             if (player->inventory_infinite_element != EL_UNDEFINED &&
10052                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10053                                      action_arg_element_raw))
10054               player->inventory_infinite_element = EL_UNDEFINED;
10055
10056             for (k = 0, j = 0; j < player->inventory_size; j++)
10057             {
10058               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10059                                         action_arg_element_raw))
10060                 player->inventory_element[k++] = player->inventory_element[j];
10061             }
10062
10063             player->inventory_size = k;
10064           }
10065           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10066           {
10067             if (player->inventory_size > 0)
10068             {
10069               for (j = 0; j < player->inventory_size - 1; j++)
10070                 player->inventory_element[j] = player->inventory_element[j + 1];
10071
10072               player->inventory_size--;
10073             }
10074           }
10075           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10076           {
10077             if (player->inventory_size > 0)
10078               player->inventory_size--;
10079           }
10080           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10081           {
10082             player->inventory_infinite_element = EL_UNDEFINED;
10083             player->inventory_size = 0;
10084           }
10085           else if (action_arg == CA_ARG_INVENTORY_RESET)
10086           {
10087             player->inventory_infinite_element = EL_UNDEFINED;
10088             player->inventory_size = 0;
10089
10090             if (level.use_initial_inventory[i])
10091             {
10092               for (j = 0; j < level.initial_inventory_size[i]; j++)
10093               {
10094                 int element = level.initial_inventory_content[i][j];
10095                 int collect_count = element_info[element].collect_count_initial;
10096
10097                 if (!IS_CUSTOM_ELEMENT(element))
10098                   collect_count = 1;
10099
10100                 if (collect_count == 0)
10101                   player->inventory_infinite_element = element;
10102                 else
10103                   for (k = 0; k < collect_count; k++)
10104                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10105                       player->inventory_element[player->inventory_size++] =
10106                         element;
10107               }
10108             }
10109           }
10110         }
10111       }
10112
10113       break;
10114     }
10115
10116     // ---------- CE actions  -------------------------------------------------
10117
10118     case CA_SET_CE_VALUE:
10119     {
10120       int last_ce_value = CustomValue[x][y];
10121
10122       CustomValue[x][y] = action_arg_number_new;
10123
10124       if (CustomValue[x][y] != last_ce_value)
10125       {
10126         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10127         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10128
10129         if (CustomValue[x][y] == 0)
10130         {
10131           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10132           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10133         }
10134       }
10135
10136       break;
10137     }
10138
10139     case CA_SET_CE_SCORE:
10140     {
10141       int last_ce_score = ei->collect_score;
10142
10143       ei->collect_score = action_arg_number_new;
10144
10145       if (ei->collect_score != last_ce_score)
10146       {
10147         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10148         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10149
10150         if (ei->collect_score == 0)
10151         {
10152           int xx, yy;
10153
10154           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10155           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10156
10157           /*
10158             This is a very special case that seems to be a mixture between
10159             CheckElementChange() and CheckTriggeredElementChange(): while
10160             the first one only affects single elements that are triggered
10161             directly, the second one affects multiple elements in the playfield
10162             that are triggered indirectly by another element. This is a third
10163             case: Changing the CE score always affects multiple identical CEs,
10164             so every affected CE must be checked, not only the single CE for
10165             which the CE score was changed in the first place (as every instance
10166             of that CE shares the same CE score, and therefore also can change)!
10167           */
10168           SCAN_PLAYFIELD(xx, yy)
10169           {
10170             if (Feld[xx][yy] == element)
10171               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10172                                  CE_SCORE_GETS_ZERO);
10173           }
10174         }
10175       }
10176
10177       break;
10178     }
10179
10180     case CA_SET_CE_ARTWORK:
10181     {
10182       int artwork_element = action_arg_element;
10183       boolean reset_frame = FALSE;
10184       int xx, yy;
10185
10186       if (action_arg == CA_ARG_ELEMENT_RESET)
10187         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10188                            element);
10189
10190       if (ei->gfx_element != artwork_element)
10191         reset_frame = TRUE;
10192
10193       ei->gfx_element = artwork_element;
10194
10195       SCAN_PLAYFIELD(xx, yy)
10196       {
10197         if (Feld[xx][yy] == element)
10198         {
10199           if (reset_frame)
10200           {
10201             ResetGfxAnimation(xx, yy);
10202             ResetRandomAnimationValue(xx, yy);
10203           }
10204
10205           TEST_DrawLevelField(xx, yy);
10206         }
10207       }
10208
10209       break;
10210     }
10211
10212     // ---------- engine actions  ---------------------------------------------
10213
10214     case CA_SET_ENGINE_SCAN_MODE:
10215     {
10216       InitPlayfieldScanMode(action_arg);
10217
10218       break;
10219     }
10220
10221     default:
10222       break;
10223   }
10224 }
10225
10226 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10227 {
10228   int old_element = Feld[x][y];
10229   int new_element = GetElementFromGroupElement(element);
10230   int previous_move_direction = MovDir[x][y];
10231   int last_ce_value = CustomValue[x][y];
10232   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10233   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10234   boolean add_player_onto_element = (new_element_is_player &&
10235                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10236                                      IS_WALKABLE(old_element));
10237
10238   if (!add_player_onto_element)
10239   {
10240     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10241       RemoveMovingField(x, y);
10242     else
10243       RemoveField(x, y);
10244
10245     Feld[x][y] = new_element;
10246
10247     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10248       MovDir[x][y] = previous_move_direction;
10249
10250     if (element_info[new_element].use_last_ce_value)
10251       CustomValue[x][y] = last_ce_value;
10252
10253     InitField_WithBug1(x, y, FALSE);
10254
10255     new_element = Feld[x][y];   // element may have changed
10256
10257     ResetGfxAnimation(x, y);
10258     ResetRandomAnimationValue(x, y);
10259
10260     TEST_DrawLevelField(x, y);
10261
10262     if (GFX_CRUMBLED(new_element))
10263       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10264   }
10265
10266   // check if element under the player changes from accessible to unaccessible
10267   // (needed for special case of dropping element which then changes)
10268   // (must be checked after creating new element for walkable group elements)
10269   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10270       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10271   {
10272     Bang(x, y);
10273
10274     return;
10275   }
10276
10277   // "ChangeCount" not set yet to allow "entered by player" change one time
10278   if (new_element_is_player)
10279     RelocatePlayer(x, y, new_element);
10280
10281   if (is_change)
10282     ChangeCount[x][y]++;        // count number of changes in the same frame
10283
10284   TestIfBadThingTouchesPlayer(x, y);
10285   TestIfPlayerTouchesCustomElement(x, y);
10286   TestIfElementTouchesCustomElement(x, y);
10287 }
10288
10289 static void CreateField(int x, int y, int element)
10290 {
10291   CreateFieldExt(x, y, element, FALSE);
10292 }
10293
10294 static void CreateElementFromChange(int x, int y, int element)
10295 {
10296   element = GET_VALID_RUNTIME_ELEMENT(element);
10297
10298   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10299   {
10300     int old_element = Feld[x][y];
10301
10302     // prevent changed element from moving in same engine frame
10303     // unless both old and new element can either fall or move
10304     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10305         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10306       Stop[x][y] = TRUE;
10307   }
10308
10309   CreateFieldExt(x, y, element, TRUE);
10310 }
10311
10312 static boolean ChangeElement(int x, int y, int element, int page)
10313 {
10314   struct ElementInfo *ei = &element_info[element];
10315   struct ElementChangeInfo *change = &ei->change_page[page];
10316   int ce_value = CustomValue[x][y];
10317   int ce_score = ei->collect_score;
10318   int target_element;
10319   int old_element = Feld[x][y];
10320
10321   // always use default change event to prevent running into a loop
10322   if (ChangeEvent[x][y] == -1)
10323     ChangeEvent[x][y] = CE_DELAY;
10324
10325   if (ChangeEvent[x][y] == CE_DELAY)
10326   {
10327     // reset actual trigger element, trigger player and action element
10328     change->actual_trigger_element = EL_EMPTY;
10329     change->actual_trigger_player = EL_EMPTY;
10330     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10331     change->actual_trigger_side = CH_SIDE_NONE;
10332     change->actual_trigger_ce_value = 0;
10333     change->actual_trigger_ce_score = 0;
10334   }
10335
10336   // do not change elements more than a specified maximum number of changes
10337   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10338     return FALSE;
10339
10340   ChangeCount[x][y]++;          // count number of changes in the same frame
10341
10342   if (change->explode)
10343   {
10344     Bang(x, y);
10345
10346     return TRUE;
10347   }
10348
10349   if (change->use_target_content)
10350   {
10351     boolean complete_replace = TRUE;
10352     boolean can_replace[3][3];
10353     int xx, yy;
10354
10355     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10356     {
10357       boolean is_empty;
10358       boolean is_walkable;
10359       boolean is_diggable;
10360       boolean is_collectible;
10361       boolean is_removable;
10362       boolean is_destructible;
10363       int ex = x + xx - 1;
10364       int ey = y + yy - 1;
10365       int content_element = change->target_content.e[xx][yy];
10366       int e;
10367
10368       can_replace[xx][yy] = TRUE;
10369
10370       if (ex == x && ey == y)   // do not check changing element itself
10371         continue;
10372
10373       if (content_element == EL_EMPTY_SPACE)
10374       {
10375         can_replace[xx][yy] = FALSE;    // do not replace border with space
10376
10377         continue;
10378       }
10379
10380       if (!IN_LEV_FIELD(ex, ey))
10381       {
10382         can_replace[xx][yy] = FALSE;
10383         complete_replace = FALSE;
10384
10385         continue;
10386       }
10387
10388       e = Feld[ex][ey];
10389
10390       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10391         e = MovingOrBlocked2Element(ex, ey);
10392
10393       is_empty = (IS_FREE(ex, ey) ||
10394                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10395
10396       is_walkable     = (is_empty || IS_WALKABLE(e));
10397       is_diggable     = (is_empty || IS_DIGGABLE(e));
10398       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10399       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10400       is_removable    = (is_diggable || is_collectible);
10401
10402       can_replace[xx][yy] =
10403         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10404           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10405           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10406           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10407           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10408           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10409          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10410
10411       if (!can_replace[xx][yy])
10412         complete_replace = FALSE;
10413     }
10414
10415     if (!change->only_if_complete || complete_replace)
10416     {
10417       boolean something_has_changed = FALSE;
10418
10419       if (change->only_if_complete && change->use_random_replace &&
10420           RND(100) < change->random_percentage)
10421         return FALSE;
10422
10423       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10424       {
10425         int ex = x + xx - 1;
10426         int ey = y + yy - 1;
10427         int content_element;
10428
10429         if (can_replace[xx][yy] && (!change->use_random_replace ||
10430                                     RND(100) < change->random_percentage))
10431         {
10432           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10433             RemoveMovingField(ex, ey);
10434
10435           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10436
10437           content_element = change->target_content.e[xx][yy];
10438           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10439                                               ce_value, ce_score);
10440
10441           CreateElementFromChange(ex, ey, target_element);
10442
10443           something_has_changed = TRUE;
10444
10445           // for symmetry reasons, freeze newly created border elements
10446           if (ex != x || ey != y)
10447             Stop[ex][ey] = TRUE;        // no more moving in this frame
10448         }
10449       }
10450
10451       if (something_has_changed)
10452       {
10453         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10454         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10455       }
10456     }
10457   }
10458   else
10459   {
10460     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10461                                         ce_value, ce_score);
10462
10463     if (element == EL_DIAGONAL_GROWING ||
10464         element == EL_DIAGONAL_SHRINKING)
10465     {
10466       target_element = Store[x][y];
10467
10468       Store[x][y] = EL_EMPTY;
10469     }
10470
10471     CreateElementFromChange(x, y, target_element);
10472
10473     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10474     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10475   }
10476
10477   // this uses direct change before indirect change
10478   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10479
10480   return TRUE;
10481 }
10482
10483 static void HandleElementChange(int x, int y, int page)
10484 {
10485   int element = MovingOrBlocked2Element(x, y);
10486   struct ElementInfo *ei = &element_info[element];
10487   struct ElementChangeInfo *change = &ei->change_page[page];
10488   boolean handle_action_before_change = FALSE;
10489
10490 #ifdef DEBUG
10491   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10492       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10493   {
10494     printf("\n\n");
10495     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10496            x, y, element, element_info[element].token_name);
10497     printf("HandleElementChange(): This should never happen!\n");
10498     printf("\n\n");
10499   }
10500 #endif
10501
10502   // this can happen with classic bombs on walkable, changing elements
10503   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10504   {
10505     return;
10506   }
10507
10508   if (ChangeDelay[x][y] == 0)           // initialize element change
10509   {
10510     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10511
10512     if (change->can_change)
10513     {
10514       // !!! not clear why graphic animation should be reset at all here !!!
10515       // !!! UPDATE: but is needed for correct Snake Bite tail animation !!!
10516       // !!! SOLUTION: do not reset if graphics engine set to 4 or above !!!
10517
10518       /*
10519         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10520
10521         When using an animation frame delay of 1 (this only happens with
10522         "sp_zonk.moving.left/right" in the classic graphics), the default
10523         (non-moving) animation shows wrong animation frames (while the
10524         moving animation, like "sp_zonk.moving.left/right", is correct,
10525         so this graphical bug never shows up with the classic graphics).
10526         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10527         be drawn instead of the correct frames 0,1,2,3. This is caused by
10528         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10529         an element change: First when the change delay ("ChangeDelay[][]")
10530         counter has reached zero after decrementing, then a second time in
10531         the next frame (after "GfxFrame[][]" was already incremented) when
10532         "ChangeDelay[][]" is reset to the initial delay value again.
10533
10534         This causes frame 0 to be drawn twice, while the last frame won't
10535         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10536
10537         As some animations may already be cleverly designed around this bug
10538         (at least the "Snake Bite" snake tail animation does this), it cannot
10539         simply be fixed here without breaking such existing animations.
10540         Unfortunately, it cannot easily be detected if a graphics set was
10541         designed "before" or "after" the bug was fixed. As a workaround,
10542         a new graphics set option "game.graphics_engine_version" was added
10543         to be able to specify the game's major release version for which the
10544         graphics set was designed, which can then be used to decide if the
10545         bugfix should be used (version 4 and above) or not (version 3 or
10546         below, or if no version was specified at all, as with old sets).
10547
10548         (The wrong/fixed animation frames can be tested with the test level set
10549         "test_gfxframe" and level "000", which contains a specially prepared
10550         custom element at level position (x/y) == (11/9) which uses the zonk
10551         animation mentioned above. Using "game.graphics_engine_version: 4"
10552         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10553         This can also be seen from the debug output for this test element.)
10554       */
10555
10556       // when a custom element is about to change (for example by change delay),
10557       // do not reset graphic animation when the custom element is moving
10558       if (game.graphics_engine_version < 4 &&
10559           !IS_MOVING(x, y))
10560       {
10561         ResetGfxAnimation(x, y);
10562         ResetRandomAnimationValue(x, y);
10563       }
10564
10565       if (change->pre_change_function)
10566         change->pre_change_function(x, y);
10567     }
10568   }
10569
10570   ChangeDelay[x][y]--;
10571
10572   if (ChangeDelay[x][y] != 0)           // continue element change
10573   {
10574     if (change->can_change)
10575     {
10576       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10577
10578       if (IS_ANIMATED(graphic))
10579         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10580
10581       if (change->change_function)
10582         change->change_function(x, y);
10583     }
10584   }
10585   else                                  // finish element change
10586   {
10587     if (ChangePage[x][y] != -1)         // remember page from delayed change
10588     {
10589       page = ChangePage[x][y];
10590       ChangePage[x][y] = -1;
10591
10592       change = &ei->change_page[page];
10593     }
10594
10595     if (IS_MOVING(x, y))                // never change a running system ;-)
10596     {
10597       ChangeDelay[x][y] = 1;            // try change after next move step
10598       ChangePage[x][y] = page;          // remember page to use for change
10599
10600       return;
10601     }
10602
10603     // special case: set new level random seed before changing element
10604     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10605       handle_action_before_change = TRUE;
10606
10607     if (change->has_action && handle_action_before_change)
10608       ExecuteCustomElementAction(x, y, element, page);
10609
10610     if (change->can_change)
10611     {
10612       if (ChangeElement(x, y, element, page))
10613       {
10614         if (change->post_change_function)
10615           change->post_change_function(x, y);
10616       }
10617     }
10618
10619     if (change->has_action && !handle_action_before_change)
10620       ExecuteCustomElementAction(x, y, element, page);
10621   }
10622 }
10623
10624 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10625                                               int trigger_element,
10626                                               int trigger_event,
10627                                               int trigger_player,
10628                                               int trigger_side,
10629                                               int trigger_page)
10630 {
10631   boolean change_done_any = FALSE;
10632   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10633   int i;
10634
10635   if (!(trigger_events[trigger_element][trigger_event]))
10636     return FALSE;
10637
10638   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10639
10640   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10641   {
10642     int element = EL_CUSTOM_START + i;
10643     boolean change_done = FALSE;
10644     int p;
10645
10646     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10647         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10648       continue;
10649
10650     for (p = 0; p < element_info[element].num_change_pages; p++)
10651     {
10652       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10653
10654       if (change->can_change_or_has_action &&
10655           change->has_event[trigger_event] &&
10656           change->trigger_side & trigger_side &&
10657           change->trigger_player & trigger_player &&
10658           change->trigger_page & trigger_page_bits &&
10659           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10660       {
10661         change->actual_trigger_element = trigger_element;
10662         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10663         change->actual_trigger_player_bits = trigger_player;
10664         change->actual_trigger_side = trigger_side;
10665         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10666         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10667
10668         if ((change->can_change && !change_done) || change->has_action)
10669         {
10670           int x, y;
10671
10672           SCAN_PLAYFIELD(x, y)
10673           {
10674             if (Feld[x][y] == element)
10675             {
10676               if (change->can_change && !change_done)
10677               {
10678                 // if element already changed in this frame, not only prevent
10679                 // another element change (checked in ChangeElement()), but
10680                 // also prevent additional element actions for this element
10681
10682                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10683                     !level.use_action_after_change_bug)
10684                   continue;
10685
10686                 ChangeDelay[x][y] = 1;
10687                 ChangeEvent[x][y] = trigger_event;
10688
10689                 HandleElementChange(x, y, p);
10690               }
10691               else if (change->has_action)
10692               {
10693                 // if element already changed in this frame, not only prevent
10694                 // another element change (checked in ChangeElement()), but
10695                 // also prevent additional element actions for this element
10696
10697                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10698                     !level.use_action_after_change_bug)
10699                   continue;
10700
10701                 ExecuteCustomElementAction(x, y, element, p);
10702                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10703               }
10704             }
10705           }
10706
10707           if (change->can_change)
10708           {
10709             change_done = TRUE;
10710             change_done_any = TRUE;
10711           }
10712         }
10713       }
10714     }
10715   }
10716
10717   RECURSION_LOOP_DETECTION_END();
10718
10719   return change_done_any;
10720 }
10721
10722 static boolean CheckElementChangeExt(int x, int y,
10723                                      int element,
10724                                      int trigger_element,
10725                                      int trigger_event,
10726                                      int trigger_player,
10727                                      int trigger_side)
10728 {
10729   boolean change_done = FALSE;
10730   int p;
10731
10732   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10733       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10734     return FALSE;
10735
10736   if (Feld[x][y] == EL_BLOCKED)
10737   {
10738     Blocked2Moving(x, y, &x, &y);
10739     element = Feld[x][y];
10740   }
10741
10742   // check if element has already changed or is about to change after moving
10743   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10744        Feld[x][y] != element) ||
10745
10746       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10747        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10748         ChangePage[x][y] != -1)))
10749     return FALSE;
10750
10751   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10752
10753   for (p = 0; p < element_info[element].num_change_pages; p++)
10754   {
10755     struct ElementChangeInfo *change = &element_info[element].change_page[p];
10756
10757     /* check trigger element for all events where the element that is checked
10758        for changing interacts with a directly adjacent element -- this is
10759        different to element changes that affect other elements to change on the
10760        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10761     boolean check_trigger_element =
10762       (trigger_event == CE_TOUCHING_X ||
10763        trigger_event == CE_HITTING_X ||
10764        trigger_event == CE_HIT_BY_X ||
10765        trigger_event == CE_DIGGING_X); // this one was forgotten until 3.2.3
10766
10767     if (change->can_change_or_has_action &&
10768         change->has_event[trigger_event] &&
10769         change->trigger_side & trigger_side &&
10770         change->trigger_player & trigger_player &&
10771         (!check_trigger_element ||
10772          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10773     {
10774       change->actual_trigger_element = trigger_element;
10775       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10776       change->actual_trigger_player_bits = trigger_player;
10777       change->actual_trigger_side = trigger_side;
10778       change->actual_trigger_ce_value = CustomValue[x][y];
10779       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10780
10781       // special case: trigger element not at (x,y) position for some events
10782       if (check_trigger_element)
10783       {
10784         static struct
10785         {
10786           int dx, dy;
10787         } move_xy[] =
10788           {
10789             {  0,  0 },
10790             { -1,  0 },
10791             { +1,  0 },
10792             {  0,  0 },
10793             {  0, -1 },
10794             {  0,  0 }, { 0, 0 }, { 0, 0 },
10795             {  0, +1 }
10796           };
10797
10798         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10799         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10800
10801         change->actual_trigger_ce_value = CustomValue[xx][yy];
10802         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10803       }
10804
10805       if (change->can_change && !change_done)
10806       {
10807         ChangeDelay[x][y] = 1;
10808         ChangeEvent[x][y] = trigger_event;
10809
10810         HandleElementChange(x, y, p);
10811
10812         change_done = TRUE;
10813       }
10814       else if (change->has_action)
10815       {
10816         ExecuteCustomElementAction(x, y, element, p);
10817         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10818       }
10819     }
10820   }
10821
10822   RECURSION_LOOP_DETECTION_END();
10823
10824   return change_done;
10825 }
10826
10827 static void PlayPlayerSound(struct PlayerInfo *player)
10828 {
10829   int jx = player->jx, jy = player->jy;
10830   int sound_element = player->artwork_element;
10831   int last_action = player->last_action_waiting;
10832   int action = player->action_waiting;
10833
10834   if (player->is_waiting)
10835   {
10836     if (action != last_action)
10837       PlayLevelSoundElementAction(jx, jy, sound_element, action);
10838     else
10839       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10840   }
10841   else
10842   {
10843     if (action != last_action)
10844       StopSound(element_info[sound_element].sound[last_action]);
10845
10846     if (last_action == ACTION_SLEEPING)
10847       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10848   }
10849 }
10850
10851 static void PlayAllPlayersSound(void)
10852 {
10853   int i;
10854
10855   for (i = 0; i < MAX_PLAYERS; i++)
10856     if (stored_player[i].active)
10857       PlayPlayerSound(&stored_player[i]);
10858 }
10859
10860 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10861 {
10862   boolean last_waiting = player->is_waiting;
10863   int move_dir = player->MovDir;
10864
10865   player->dir_waiting = move_dir;
10866   player->last_action_waiting = player->action_waiting;
10867
10868   if (is_waiting)
10869   {
10870     if (!last_waiting)          // not waiting -> waiting
10871     {
10872       player->is_waiting = TRUE;
10873
10874       player->frame_counter_bored =
10875         FrameCounter +
10876         game.player_boring_delay_fixed +
10877         GetSimpleRandom(game.player_boring_delay_random);
10878       player->frame_counter_sleeping =
10879         FrameCounter +
10880         game.player_sleeping_delay_fixed +
10881         GetSimpleRandom(game.player_sleeping_delay_random);
10882
10883       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10884     }
10885
10886     if (game.player_sleeping_delay_fixed +
10887         game.player_sleeping_delay_random > 0 &&
10888         player->anim_delay_counter == 0 &&
10889         player->post_delay_counter == 0 &&
10890         FrameCounter >= player->frame_counter_sleeping)
10891       player->is_sleeping = TRUE;
10892     else if (game.player_boring_delay_fixed +
10893              game.player_boring_delay_random > 0 &&
10894              FrameCounter >= player->frame_counter_bored)
10895       player->is_bored = TRUE;
10896
10897     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10898                               player->is_bored ? ACTION_BORING :
10899                               ACTION_WAITING);
10900
10901     if (player->is_sleeping && player->use_murphy)
10902     {
10903       // special case for sleeping Murphy when leaning against non-free tile
10904
10905       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10906           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10907            !IS_MOVING(player->jx - 1, player->jy)))
10908         move_dir = MV_LEFT;
10909       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10910                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10911                 !IS_MOVING(player->jx + 1, player->jy)))
10912         move_dir = MV_RIGHT;
10913       else
10914         player->is_sleeping = FALSE;
10915
10916       player->dir_waiting = move_dir;
10917     }
10918
10919     if (player->is_sleeping)
10920     {
10921       if (player->num_special_action_sleeping > 0)
10922       {
10923         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10924         {
10925           int last_special_action = player->special_action_sleeping;
10926           int num_special_action = player->num_special_action_sleeping;
10927           int special_action =
10928             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10929              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10930              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10931              last_special_action + 1 : ACTION_SLEEPING);
10932           int special_graphic =
10933             el_act_dir2img(player->artwork_element, special_action, move_dir);
10934
10935           player->anim_delay_counter =
10936             graphic_info[special_graphic].anim_delay_fixed +
10937             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10938           player->post_delay_counter =
10939             graphic_info[special_graphic].post_delay_fixed +
10940             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10941
10942           player->special_action_sleeping = special_action;
10943         }
10944
10945         if (player->anim_delay_counter > 0)
10946         {
10947           player->action_waiting = player->special_action_sleeping;
10948           player->anim_delay_counter--;
10949         }
10950         else if (player->post_delay_counter > 0)
10951         {
10952           player->post_delay_counter--;
10953         }
10954       }
10955     }
10956     else if (player->is_bored)
10957     {
10958       if (player->num_special_action_bored > 0)
10959       {
10960         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10961         {
10962           int special_action =
10963             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10964           int special_graphic =
10965             el_act_dir2img(player->artwork_element, special_action, move_dir);
10966
10967           player->anim_delay_counter =
10968             graphic_info[special_graphic].anim_delay_fixed +
10969             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10970           player->post_delay_counter =
10971             graphic_info[special_graphic].post_delay_fixed +
10972             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10973
10974           player->special_action_bored = special_action;
10975         }
10976
10977         if (player->anim_delay_counter > 0)
10978         {
10979           player->action_waiting = player->special_action_bored;
10980           player->anim_delay_counter--;
10981         }
10982         else if (player->post_delay_counter > 0)
10983         {
10984           player->post_delay_counter--;
10985         }
10986       }
10987     }
10988   }
10989   else if (last_waiting)        // waiting -> not waiting
10990   {
10991     player->is_waiting = FALSE;
10992     player->is_bored = FALSE;
10993     player->is_sleeping = FALSE;
10994
10995     player->frame_counter_bored = -1;
10996     player->frame_counter_sleeping = -1;
10997
10998     player->anim_delay_counter = 0;
10999     player->post_delay_counter = 0;
11000
11001     player->dir_waiting = player->MovDir;
11002     player->action_waiting = ACTION_DEFAULT;
11003
11004     player->special_action_bored = ACTION_DEFAULT;
11005     player->special_action_sleeping = ACTION_DEFAULT;
11006   }
11007 }
11008
11009 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
11010 {
11011   if ((!player->is_moving  && player->was_moving) ||
11012       (player->MovPos == 0 && player->was_moving) ||
11013       (player->is_snapping && !player->was_snapping) ||
11014       (player->is_dropping && !player->was_dropping))
11015   {
11016     if (!CheckSaveEngineSnapshotToList())
11017       return;
11018
11019     player->was_moving = FALSE;
11020     player->was_snapping = TRUE;
11021     player->was_dropping = TRUE;
11022   }
11023   else
11024   {
11025     if (player->is_moving)
11026       player->was_moving = TRUE;
11027
11028     if (!player->is_snapping)
11029       player->was_snapping = FALSE;
11030
11031     if (!player->is_dropping)
11032       player->was_dropping = FALSE;
11033   }
11034 }
11035
11036 static void CheckSingleStepMode(struct PlayerInfo *player)
11037 {
11038   if (tape.single_step && tape.recording && !tape.pausing)
11039   {
11040     /* as it is called "single step mode", just return to pause mode when the
11041        player stopped moving after one tile (or never starts moving at all) */
11042     if (!player->is_moving &&
11043         !player->is_pushing &&
11044         !player->is_dropping_pressed)
11045     {
11046       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11047       SnapField(player, 0, 0);                  // stop snapping
11048     }
11049   }
11050
11051   CheckSaveEngineSnapshot(player);
11052 }
11053
11054 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11055 {
11056   int left      = player_action & JOY_LEFT;
11057   int right     = player_action & JOY_RIGHT;
11058   int up        = player_action & JOY_UP;
11059   int down      = player_action & JOY_DOWN;
11060   int button1   = player_action & JOY_BUTTON_1;
11061   int button2   = player_action & JOY_BUTTON_2;
11062   int dx        = (left ? -1 : right ? 1 : 0);
11063   int dy        = (up   ? -1 : down  ? 1 : 0);
11064
11065   if (!player->active || tape.pausing)
11066     return 0;
11067
11068   if (player_action)
11069   {
11070     if (button1)
11071       SnapField(player, dx, dy);
11072     else
11073     {
11074       if (button2)
11075         DropElement(player);
11076
11077       MovePlayer(player, dx, dy);
11078     }
11079
11080     CheckSingleStepMode(player);
11081
11082     SetPlayerWaiting(player, FALSE);
11083
11084     return player_action;
11085   }
11086   else
11087   {
11088     // no actions for this player (no input at player's configured device)
11089
11090     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11091     SnapField(player, 0, 0);
11092     CheckGravityMovementWhenNotMoving(player);
11093
11094     if (player->MovPos == 0)
11095       SetPlayerWaiting(player, TRUE);
11096
11097     if (player->MovPos == 0)    // needed for tape.playing
11098       player->is_moving = FALSE;
11099
11100     player->is_dropping = FALSE;
11101     player->is_dropping_pressed = FALSE;
11102     player->drop_pressed_delay = 0;
11103
11104     CheckSingleStepMode(player);
11105
11106     return 0;
11107   }
11108 }
11109
11110 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11111                                          byte *tape_action)
11112 {
11113   if (!tape.use_mouse)
11114     return;
11115
11116   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11117   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11118   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11119 }
11120
11121 static void SetTapeActionFromMouseAction(byte *tape_action,
11122                                          struct MouseActionInfo *mouse_action)
11123 {
11124   if (!tape.use_mouse)
11125     return;
11126
11127   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11128   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11129   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11130 }
11131
11132 static void CheckLevelSolved(void)
11133 {
11134   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11135   {
11136     if (game_em.level_solved &&
11137         !game_em.game_over)                             // game won
11138     {
11139       LevelSolved();
11140
11141       game_em.game_over = TRUE;
11142
11143       game.all_players_gone = TRUE;
11144     }
11145
11146     if (game_em.game_over)                              // game lost
11147       game.all_players_gone = TRUE;
11148   }
11149   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11150   {
11151     if (game_sp.level_solved &&
11152         !game_sp.game_over)                             // game won
11153     {
11154       LevelSolved();
11155
11156       game_sp.game_over = TRUE;
11157
11158       game.all_players_gone = TRUE;
11159     }
11160
11161     if (game_sp.game_over)                              // game lost
11162       game.all_players_gone = TRUE;
11163   }
11164   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11165   {
11166     if (game_mm.level_solved &&
11167         !game_mm.game_over)                             // game won
11168     {
11169       LevelSolved();
11170
11171       game_mm.game_over = TRUE;
11172
11173       game.all_players_gone = TRUE;
11174     }
11175
11176     if (game_mm.game_over)                              // game lost
11177       game.all_players_gone = TRUE;
11178   }
11179 }
11180
11181 static void CheckLevelTime(void)
11182 {
11183   int i;
11184
11185   if (TimeFrames >= FRAMES_PER_SECOND)
11186   {
11187     TimeFrames = 0;
11188     TapeTime++;
11189
11190     for (i = 0; i < MAX_PLAYERS; i++)
11191     {
11192       struct PlayerInfo *player = &stored_player[i];
11193
11194       if (SHIELD_ON(player))
11195       {
11196         player->shield_normal_time_left--;
11197
11198         if (player->shield_deadly_time_left > 0)
11199           player->shield_deadly_time_left--;
11200       }
11201     }
11202
11203     if (!game.LevelSolved && !level.use_step_counter)
11204     {
11205       TimePlayed++;
11206
11207       if (TimeLeft > 0)
11208       {
11209         TimeLeft--;
11210
11211         if (TimeLeft <= 10 && setup.time_limit)
11212           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11213
11214         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11215            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11216
11217         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11218
11219         if (!TimeLeft && setup.time_limit)
11220         {
11221           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11222             level.native_em_level->lev->killed_out_of_time = TRUE;
11223           else
11224             for (i = 0; i < MAX_PLAYERS; i++)
11225               KillPlayer(&stored_player[i]);
11226         }
11227       }
11228       else if (game.no_time_limit && !game.all_players_gone)
11229       {
11230         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11231       }
11232
11233       level.native_em_level->lev->time =
11234         (game.no_time_limit ? TimePlayed : TimeLeft);
11235     }
11236
11237     if (tape.recording || tape.playing)
11238       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11239   }
11240
11241   if (tape.recording || tape.playing)
11242     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11243
11244   UpdateAndDisplayGameControlValues();
11245 }
11246
11247 void AdvanceFrameAndPlayerCounters(int player_nr)
11248 {
11249   int i;
11250
11251   // advance frame counters (global frame counter and time frame counter)
11252   FrameCounter++;
11253   TimeFrames++;
11254
11255   // advance player counters (counters for move delay, move animation etc.)
11256   for (i = 0; i < MAX_PLAYERS; i++)
11257   {
11258     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11259     int move_delay_value = stored_player[i].move_delay_value;
11260     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11261
11262     if (!advance_player_counters)       // not all players may be affected
11263       continue;
11264
11265     if (move_frames == 0)       // less than one move per game frame
11266     {
11267       int stepsize = TILEX / move_delay_value;
11268       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11269       int count = (stored_player[i].is_moving ?
11270                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11271
11272       if (count % delay == 0)
11273         move_frames = 1;
11274     }
11275
11276     stored_player[i].Frame += move_frames;
11277
11278     if (stored_player[i].MovPos != 0)
11279       stored_player[i].StepFrame += move_frames;
11280
11281     if (stored_player[i].move_delay > 0)
11282       stored_player[i].move_delay--;
11283
11284     // due to bugs in previous versions, counter must count up, not down
11285     if (stored_player[i].push_delay != -1)
11286       stored_player[i].push_delay++;
11287
11288     if (stored_player[i].drop_delay > 0)
11289       stored_player[i].drop_delay--;
11290
11291     if (stored_player[i].is_dropping_pressed)
11292       stored_player[i].drop_pressed_delay++;
11293   }
11294 }
11295
11296 void StartGameActions(boolean init_network_game, boolean record_tape,
11297                       int random_seed)
11298 {
11299   unsigned int new_random_seed = InitRND(random_seed);
11300
11301   if (record_tape)
11302     TapeStartRecording(new_random_seed);
11303
11304   if (init_network_game)
11305   {
11306     SendToServer_LevelFile();
11307     SendToServer_StartPlaying();
11308
11309     return;
11310   }
11311
11312   InitGame();
11313 }
11314
11315 static void GameActionsExt(void)
11316 {
11317 #if 0
11318   static unsigned int game_frame_delay = 0;
11319 #endif
11320   unsigned int game_frame_delay_value;
11321   byte *recorded_player_action;
11322   byte summarized_player_action = 0;
11323   byte tape_action[MAX_PLAYERS];
11324   int i;
11325
11326   // detect endless loops, caused by custom element programming
11327   if (recursion_loop_detected && recursion_loop_depth == 0)
11328   {
11329     char *message = getStringCat3("Internal Error! Element ",
11330                                   EL_NAME(recursion_loop_element),
11331                                   " caused endless loop! Quit the game?");
11332
11333     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11334           EL_NAME(recursion_loop_element));
11335
11336     RequestQuitGameExt(FALSE, level_editor_test_game, message);
11337
11338     recursion_loop_detected = FALSE;    // if game should be continued
11339
11340     free(message);
11341
11342     return;
11343   }
11344
11345   if (game.restart_level)
11346     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11347
11348   CheckLevelSolved();
11349
11350   if (game.LevelSolved && !game.LevelSolved_GameEnd)
11351     GameWon();
11352
11353   if (game.all_players_gone && !TAPE_IS_STOPPED(tape))
11354     TapeStop();
11355
11356   if (game_status != GAME_MODE_PLAYING)         // status might have changed
11357     return;
11358
11359   game_frame_delay_value =
11360     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11361
11362   if (tape.playing && tape.warp_forward && !tape.pausing)
11363     game_frame_delay_value = 0;
11364
11365   SetVideoFrameDelay(game_frame_delay_value);
11366
11367 #if 0
11368 #if 0
11369   // ---------- main game synchronization point ----------
11370
11371   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11372
11373   printf("::: skip == %d\n", skip);
11374
11375 #else
11376   // ---------- main game synchronization point ----------
11377
11378   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11379 #endif
11380 #endif
11381
11382   if (network_playing && !network_player_action_received)
11383   {
11384     // try to get network player actions in time
11385
11386     // last chance to get network player actions without main loop delay
11387     HandleNetworking();
11388
11389     // game was quit by network peer
11390     if (game_status != GAME_MODE_PLAYING)
11391       return;
11392
11393     // check if network player actions still missing and game still running
11394     if (!network_player_action_received && !checkGameEnded())
11395       return;           // failed to get network player actions in time
11396
11397     // do not yet reset "network_player_action_received" (for tape.pausing)
11398   }
11399
11400   if (tape.pausing)
11401     return;
11402
11403   // at this point we know that we really continue executing the game
11404
11405   network_player_action_received = FALSE;
11406
11407   // when playing tape, read previously recorded player input from tape data
11408   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11409
11410   local_player->effective_mouse_action = local_player->mouse_action;
11411
11412   if (recorded_player_action != NULL)
11413     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11414                                  recorded_player_action);
11415
11416   // TapePlayAction() may return NULL when toggling to "pause before death"
11417   if (tape.pausing)
11418     return;
11419
11420   if (tape.set_centered_player)
11421   {
11422     game.centered_player_nr_next = tape.centered_player_nr_next;
11423     game.set_centered_player = TRUE;
11424   }
11425
11426   for (i = 0; i < MAX_PLAYERS; i++)
11427   {
11428     summarized_player_action |= stored_player[i].action;
11429
11430     if (!network_playing && (game.team_mode || tape.playing))
11431       stored_player[i].effective_action = stored_player[i].action;
11432   }
11433
11434   if (network_playing && !checkGameEnded())
11435     SendToServer_MovePlayer(summarized_player_action);
11436
11437   // summarize all actions at local players mapped input device position
11438   // (this allows using different input devices in single player mode)
11439   if (!network.enabled && !game.team_mode)
11440     stored_player[map_player_action[local_player->index_nr]].effective_action =
11441       summarized_player_action;
11442
11443   if (tape.recording &&
11444       setup.team_mode &&
11445       setup.input_on_focus &&
11446       game.centered_player_nr != -1)
11447   {
11448     for (i = 0; i < MAX_PLAYERS; i++)
11449       stored_player[i].effective_action =
11450         (i == game.centered_player_nr ? summarized_player_action : 0);
11451   }
11452
11453   if (recorded_player_action != NULL)
11454     for (i = 0; i < MAX_PLAYERS; i++)
11455       stored_player[i].effective_action = recorded_player_action[i];
11456
11457   for (i = 0; i < MAX_PLAYERS; i++)
11458   {
11459     tape_action[i] = stored_player[i].effective_action;
11460
11461     /* (this may happen in the RND game engine if a player was not present on
11462        the playfield on level start, but appeared later from a custom element */
11463     if (setup.team_mode &&
11464         tape.recording &&
11465         tape_action[i] &&
11466         !tape.player_participates[i])
11467       tape.player_participates[i] = TRUE;
11468   }
11469
11470   SetTapeActionFromMouseAction(tape_action,
11471                                &local_player->effective_mouse_action);
11472
11473   // only record actions from input devices, but not programmed actions
11474   if (tape.recording)
11475     TapeRecordAction(tape_action);
11476
11477 #if USE_NEW_PLAYER_ASSIGNMENTS
11478   // !!! also map player actions in single player mode !!!
11479   // if (game.team_mode)
11480   if (1)
11481   {
11482     byte mapped_action[MAX_PLAYERS];
11483
11484 #if DEBUG_PLAYER_ACTIONS
11485     printf(":::");
11486     for (i = 0; i < MAX_PLAYERS; i++)
11487       printf(" %d, ", stored_player[i].effective_action);
11488 #endif
11489
11490     for (i = 0; i < MAX_PLAYERS; i++)
11491       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11492
11493     for (i = 0; i < MAX_PLAYERS; i++)
11494       stored_player[i].effective_action = mapped_action[i];
11495
11496 #if DEBUG_PLAYER_ACTIONS
11497     printf(" =>");
11498     for (i = 0; i < MAX_PLAYERS; i++)
11499       printf(" %d, ", stored_player[i].effective_action);
11500     printf("\n");
11501 #endif
11502   }
11503 #if DEBUG_PLAYER_ACTIONS
11504   else
11505   {
11506     printf(":::");
11507     for (i = 0; i < MAX_PLAYERS; i++)
11508       printf(" %d, ", stored_player[i].effective_action);
11509     printf("\n");
11510   }
11511 #endif
11512 #endif
11513
11514   for (i = 0; i < MAX_PLAYERS; i++)
11515   {
11516     // allow engine snapshot in case of changed movement attempt
11517     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11518         (stored_player[i].effective_action & KEY_MOTION))
11519       game.snapshot.changed_action = TRUE;
11520
11521     // allow engine snapshot in case of snapping/dropping attempt
11522     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11523         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11524       game.snapshot.changed_action = TRUE;
11525
11526     game.snapshot.last_action[i] = stored_player[i].effective_action;
11527   }
11528
11529   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11530   {
11531     GameActions_EM_Main();
11532   }
11533   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11534   {
11535     GameActions_SP_Main();
11536   }
11537   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11538   {
11539     GameActions_MM_Main();
11540   }
11541   else
11542   {
11543     GameActions_RND_Main();
11544   }
11545
11546   BlitScreenToBitmap(backbuffer);
11547
11548   CheckLevelSolved();
11549   CheckLevelTime();
11550
11551   AdvanceFrameAndPlayerCounters(-1);    // advance counters for all players
11552
11553   if (global.show_frames_per_second)
11554   {
11555     static unsigned int fps_counter = 0;
11556     static int fps_frames = 0;
11557     unsigned int fps_delay_ms = Counter() - fps_counter;
11558
11559     fps_frames++;
11560
11561     if (fps_delay_ms >= 500)    // calculate FPS every 0.5 seconds
11562     {
11563       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11564
11565       fps_frames = 0;
11566       fps_counter = Counter();
11567
11568       // always draw FPS to screen after FPS value was updated
11569       redraw_mask |= REDRAW_FPS;
11570     }
11571
11572     // only draw FPS if no screen areas are deactivated (invisible warp mode)
11573     if (GetDrawDeactivationMask() == REDRAW_NONE)
11574       redraw_mask |= REDRAW_FPS;
11575   }
11576 }
11577
11578 static void GameActions_CheckSaveEngineSnapshot(void)
11579 {
11580   if (!game.snapshot.save_snapshot)
11581     return;
11582
11583   // clear flag for saving snapshot _before_ saving snapshot
11584   game.snapshot.save_snapshot = FALSE;
11585
11586   SaveEngineSnapshotToList();
11587 }
11588
11589 void GameActions(void)
11590 {
11591   GameActionsExt();
11592
11593   GameActions_CheckSaveEngineSnapshot();
11594 }
11595
11596 void GameActions_EM_Main(void)
11597 {
11598   byte effective_action[MAX_PLAYERS];
11599   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11600   int i;
11601
11602   for (i = 0; i < MAX_PLAYERS; i++)
11603     effective_action[i] = stored_player[i].effective_action;
11604
11605   GameActions_EM(effective_action, warp_mode);
11606 }
11607
11608 void GameActions_SP_Main(void)
11609 {
11610   byte effective_action[MAX_PLAYERS];
11611   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11612   int i;
11613
11614   for (i = 0; i < MAX_PLAYERS; i++)
11615     effective_action[i] = stored_player[i].effective_action;
11616
11617   GameActions_SP(effective_action, warp_mode);
11618
11619   for (i = 0; i < MAX_PLAYERS; i++)
11620   {
11621     if (stored_player[i].force_dropping)
11622       stored_player[i].action |= KEY_BUTTON_DROP;
11623
11624     stored_player[i].force_dropping = FALSE;
11625   }
11626 }
11627
11628 void GameActions_MM_Main(void)
11629 {
11630   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11631
11632   GameActions_MM(local_player->effective_mouse_action, warp_mode);
11633 }
11634
11635 void GameActions_RND_Main(void)
11636 {
11637   GameActions_RND();
11638 }
11639
11640 void GameActions_RND(void)
11641 {
11642   int magic_wall_x = 0, magic_wall_y = 0;
11643   int i, x, y, element, graphic, last_gfx_frame;
11644
11645   InitPlayfieldScanModeVars();
11646
11647   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11648   {
11649     SCAN_PLAYFIELD(x, y)
11650     {
11651       ChangeCount[x][y] = 0;
11652       ChangeEvent[x][y] = -1;
11653     }
11654   }
11655
11656   if (game.set_centered_player)
11657   {
11658     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11659
11660     // switching to "all players" only possible if all players fit to screen
11661     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11662     {
11663       game.centered_player_nr_next = game.centered_player_nr;
11664       game.set_centered_player = FALSE;
11665     }
11666
11667     // do not switch focus to non-existing (or non-active) player
11668     if (game.centered_player_nr_next >= 0 &&
11669         !stored_player[game.centered_player_nr_next].active)
11670     {
11671       game.centered_player_nr_next = game.centered_player_nr;
11672       game.set_centered_player = FALSE;
11673     }
11674   }
11675
11676   if (game.set_centered_player &&
11677       ScreenMovPos == 0)        // screen currently aligned at tile position
11678   {
11679     int sx, sy;
11680
11681     if (game.centered_player_nr_next == -1)
11682     {
11683       setScreenCenteredToAllPlayers(&sx, &sy);
11684     }
11685     else
11686     {
11687       sx = stored_player[game.centered_player_nr_next].jx;
11688       sy = stored_player[game.centered_player_nr_next].jy;
11689     }
11690
11691     game.centered_player_nr = game.centered_player_nr_next;
11692     game.set_centered_player = FALSE;
11693
11694     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11695     DrawGameDoorValues();
11696   }
11697
11698   for (i = 0; i < MAX_PLAYERS; i++)
11699   {
11700     int actual_player_action = stored_player[i].effective_action;
11701
11702 #if 1
11703     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11704        - rnd_equinox_tetrachloride 048
11705        - rnd_equinox_tetrachloride_ii 096
11706        - rnd_emanuel_schmieg 002
11707        - doctor_sloan_ww 001, 020
11708     */
11709     if (stored_player[i].MovPos == 0)
11710       CheckGravityMovement(&stored_player[i]);
11711 #endif
11712
11713     // overwrite programmed action with tape action
11714     if (stored_player[i].programmed_action)
11715       actual_player_action = stored_player[i].programmed_action;
11716
11717     PlayerActions(&stored_player[i], actual_player_action);
11718
11719     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11720   }
11721
11722   ScrollScreen(NULL, SCROLL_GO_ON);
11723
11724   /* for backwards compatibility, the following code emulates a fixed bug that
11725      occured when pushing elements (causing elements that just made their last
11726      pushing step to already (if possible) make their first falling step in the
11727      same game frame, which is bad); this code is also needed to use the famous
11728      "spring push bug" which is used in older levels and might be wanted to be
11729      used also in newer levels, but in this case the buggy pushing code is only
11730      affecting the "spring" element and no other elements */
11731
11732   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11733   {
11734     for (i = 0; i < MAX_PLAYERS; i++)
11735     {
11736       struct PlayerInfo *player = &stored_player[i];
11737       int x = player->jx;
11738       int y = player->jy;
11739
11740       if (player->active && player->is_pushing && player->is_moving &&
11741           IS_MOVING(x, y) &&
11742           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11743            Feld[x][y] == EL_SPRING))
11744       {
11745         ContinueMoving(x, y);
11746
11747         // continue moving after pushing (this is actually a bug)
11748         if (!IS_MOVING(x, y))
11749           Stop[x][y] = FALSE;
11750       }
11751     }
11752   }
11753
11754   SCAN_PLAYFIELD(x, y)
11755   {
11756     Last[x][y] = Feld[x][y];
11757
11758     ChangeCount[x][y] = 0;
11759     ChangeEvent[x][y] = -1;
11760
11761     // this must be handled before main playfield loop
11762     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11763     {
11764       MovDelay[x][y]--;
11765       if (MovDelay[x][y] <= 0)
11766         RemoveField(x, y);
11767     }
11768
11769     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11770     {
11771       MovDelay[x][y]--;
11772       if (MovDelay[x][y] <= 0)
11773       {
11774         RemoveField(x, y);
11775         TEST_DrawLevelField(x, y);
11776
11777         TestIfElementTouchesCustomElement(x, y);        // for empty space
11778       }
11779     }
11780
11781 #if DEBUG
11782     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11783     {
11784       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11785       printf("GameActions(): This should never happen!\n");
11786
11787       ChangePage[x][y] = -1;
11788     }
11789 #endif
11790
11791     Stop[x][y] = FALSE;
11792     if (WasJustMoving[x][y] > 0)
11793       WasJustMoving[x][y]--;
11794     if (WasJustFalling[x][y] > 0)
11795       WasJustFalling[x][y]--;
11796     if (CheckCollision[x][y] > 0)
11797       CheckCollision[x][y]--;
11798     if (CheckImpact[x][y] > 0)
11799       CheckImpact[x][y]--;
11800
11801     GfxFrame[x][y]++;
11802
11803     /* reset finished pushing action (not done in ContinueMoving() to allow
11804        continuous pushing animation for elements with zero push delay) */
11805     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11806     {
11807       ResetGfxAnimation(x, y);
11808       TEST_DrawLevelField(x, y);
11809     }
11810
11811 #if DEBUG
11812     if (IS_BLOCKED(x, y))
11813     {
11814       int oldx, oldy;
11815
11816       Blocked2Moving(x, y, &oldx, &oldy);
11817       if (!IS_MOVING(oldx, oldy))
11818       {
11819         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11820         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11821         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11822         printf("GameActions(): This should never happen!\n");
11823       }
11824     }
11825 #endif
11826   }
11827
11828   SCAN_PLAYFIELD(x, y)
11829   {
11830     element = Feld[x][y];
11831     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11832     last_gfx_frame = GfxFrame[x][y];
11833
11834     ResetGfxFrame(x, y);
11835
11836     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
11837       DrawLevelGraphicAnimation(x, y, graphic);
11838
11839     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11840         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11841       ResetRandomAnimationValue(x, y);
11842
11843     SetRandomAnimationValue(x, y);
11844
11845     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11846
11847     if (IS_INACTIVE(element))
11848     {
11849       if (IS_ANIMATED(graphic))
11850         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11851
11852       continue;
11853     }
11854
11855     // this may take place after moving, so 'element' may have changed
11856     if (IS_CHANGING(x, y) &&
11857         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11858     {
11859       int page = element_info[element].event_page_nr[CE_DELAY];
11860
11861       HandleElementChange(x, y, page);
11862
11863       element = Feld[x][y];
11864       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11865     }
11866
11867     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11868     {
11869       StartMoving(x, y);
11870
11871       element = Feld[x][y];
11872       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11873
11874       if (IS_ANIMATED(graphic) &&
11875           !IS_MOVING(x, y) &&
11876           !Stop[x][y])
11877         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11878
11879       if (IS_GEM(element) || element == EL_SP_INFOTRON)
11880         TEST_DrawTwinkleOnField(x, y);
11881     }
11882     else if (element == EL_ACID)
11883     {
11884       if (!Stop[x][y])
11885         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11886     }
11887     else if ((element == EL_EXIT_OPEN ||
11888               element == EL_EM_EXIT_OPEN ||
11889               element == EL_SP_EXIT_OPEN ||
11890               element == EL_STEEL_EXIT_OPEN ||
11891               element == EL_EM_STEEL_EXIT_OPEN ||
11892               element == EL_SP_TERMINAL ||
11893               element == EL_SP_TERMINAL_ACTIVE ||
11894               element == EL_EXTRA_TIME ||
11895               element == EL_SHIELD_NORMAL ||
11896               element == EL_SHIELD_DEADLY) &&
11897              IS_ANIMATED(graphic))
11898       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11899     else if (IS_MOVING(x, y))
11900       ContinueMoving(x, y);
11901     else if (IS_ACTIVE_BOMB(element))
11902       CheckDynamite(x, y);
11903     else if (element == EL_AMOEBA_GROWING)
11904       AmoebeWaechst(x, y);
11905     else if (element == EL_AMOEBA_SHRINKING)
11906       AmoebaDisappearing(x, y);
11907
11908 #if !USE_NEW_AMOEBA_CODE
11909     else if (IS_AMOEBALIVE(element))
11910       AmoebeAbleger(x, y);
11911 #endif
11912
11913     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11914       Life(x, y);
11915     else if (element == EL_EXIT_CLOSED)
11916       CheckExit(x, y);
11917     else if (element == EL_EM_EXIT_CLOSED)
11918       CheckExitEM(x, y);
11919     else if (element == EL_STEEL_EXIT_CLOSED)
11920       CheckExitSteel(x, y);
11921     else if (element == EL_EM_STEEL_EXIT_CLOSED)
11922       CheckExitSteelEM(x, y);
11923     else if (element == EL_SP_EXIT_CLOSED)
11924       CheckExitSP(x, y);
11925     else if (element == EL_EXPANDABLE_WALL_GROWING ||
11926              element == EL_EXPANDABLE_STEELWALL_GROWING)
11927       MauerWaechst(x, y);
11928     else if (element == EL_EXPANDABLE_WALL ||
11929              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11930              element == EL_EXPANDABLE_WALL_VERTICAL ||
11931              element == EL_EXPANDABLE_WALL_ANY ||
11932              element == EL_BD_EXPANDABLE_WALL)
11933       MauerAbleger(x, y);
11934     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11935              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11936              element == EL_EXPANDABLE_STEELWALL_ANY)
11937       MauerAblegerStahl(x, y);
11938     else if (element == EL_FLAMES)
11939       CheckForDragon(x, y);
11940     else if (element == EL_EXPLOSION)
11941       ; // drawing of correct explosion animation is handled separately
11942     else if (element == EL_ELEMENT_SNAPPING ||
11943              element == EL_DIAGONAL_SHRINKING ||
11944              element == EL_DIAGONAL_GROWING)
11945     {
11946       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11947
11948       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11949     }
11950     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11951       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11952
11953     if (IS_BELT_ACTIVE(element))
11954       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11955
11956     if (game.magic_wall_active)
11957     {
11958       int jx = local_player->jx, jy = local_player->jy;
11959
11960       // play the element sound at the position nearest to the player
11961       if ((element == EL_MAGIC_WALL_FULL ||
11962            element == EL_MAGIC_WALL_ACTIVE ||
11963            element == EL_MAGIC_WALL_EMPTYING ||
11964            element == EL_BD_MAGIC_WALL_FULL ||
11965            element == EL_BD_MAGIC_WALL_ACTIVE ||
11966            element == EL_BD_MAGIC_WALL_EMPTYING ||
11967            element == EL_DC_MAGIC_WALL_FULL ||
11968            element == EL_DC_MAGIC_WALL_ACTIVE ||
11969            element == EL_DC_MAGIC_WALL_EMPTYING) &&
11970           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11971       {
11972         magic_wall_x = x;
11973         magic_wall_y = y;
11974       }
11975     }
11976   }
11977
11978 #if USE_NEW_AMOEBA_CODE
11979   // new experimental amoeba growth stuff
11980   if (!(FrameCounter % 8))
11981   {
11982     static unsigned int random = 1684108901;
11983
11984     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11985     {
11986       x = RND(lev_fieldx);
11987       y = RND(lev_fieldy);
11988       element = Feld[x][y];
11989
11990       if (!IS_PLAYER(x,y) &&
11991           (element == EL_EMPTY ||
11992            CAN_GROW_INTO(element) ||
11993            element == EL_QUICKSAND_EMPTY ||
11994            element == EL_QUICKSAND_FAST_EMPTY ||
11995            element == EL_ACID_SPLASH_LEFT ||
11996            element == EL_ACID_SPLASH_RIGHT))
11997       {
11998         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
11999             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
12000             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
12001             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
12002           Feld[x][y] = EL_AMOEBA_DROP;
12003       }
12004
12005       random = random * 129 + 1;
12006     }
12007   }
12008 #endif
12009
12010   game.explosions_delayed = FALSE;
12011
12012   SCAN_PLAYFIELD(x, y)
12013   {
12014     element = Feld[x][y];
12015
12016     if (ExplodeField[x][y])
12017       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12018     else if (element == EL_EXPLOSION)
12019       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12020
12021     ExplodeField[x][y] = EX_TYPE_NONE;
12022   }
12023
12024   game.explosions_delayed = TRUE;
12025
12026   if (game.magic_wall_active)
12027   {
12028     if (!(game.magic_wall_time_left % 4))
12029     {
12030       int element = Feld[magic_wall_x][magic_wall_y];
12031
12032       if (element == EL_BD_MAGIC_WALL_FULL ||
12033           element == EL_BD_MAGIC_WALL_ACTIVE ||
12034           element == EL_BD_MAGIC_WALL_EMPTYING)
12035         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12036       else if (element == EL_DC_MAGIC_WALL_FULL ||
12037                element == EL_DC_MAGIC_WALL_ACTIVE ||
12038                element == EL_DC_MAGIC_WALL_EMPTYING)
12039         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12040       else
12041         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12042     }
12043
12044     if (game.magic_wall_time_left > 0)
12045     {
12046       game.magic_wall_time_left--;
12047
12048       if (!game.magic_wall_time_left)
12049       {
12050         SCAN_PLAYFIELD(x, y)
12051         {
12052           element = Feld[x][y];
12053
12054           if (element == EL_MAGIC_WALL_ACTIVE ||
12055               element == EL_MAGIC_WALL_FULL)
12056           {
12057             Feld[x][y] = EL_MAGIC_WALL_DEAD;
12058             TEST_DrawLevelField(x, y);
12059           }
12060           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12061                    element == EL_BD_MAGIC_WALL_FULL)
12062           {
12063             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
12064             TEST_DrawLevelField(x, y);
12065           }
12066           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12067                    element == EL_DC_MAGIC_WALL_FULL)
12068           {
12069             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
12070             TEST_DrawLevelField(x, y);
12071           }
12072         }
12073
12074         game.magic_wall_active = FALSE;
12075       }
12076     }
12077   }
12078
12079   if (game.light_time_left > 0)
12080   {
12081     game.light_time_left--;
12082
12083     if (game.light_time_left == 0)
12084       RedrawAllLightSwitchesAndInvisibleElements();
12085   }
12086
12087   if (game.timegate_time_left > 0)
12088   {
12089     game.timegate_time_left--;
12090
12091     if (game.timegate_time_left == 0)
12092       CloseAllOpenTimegates();
12093   }
12094
12095   if (game.lenses_time_left > 0)
12096   {
12097     game.lenses_time_left--;
12098
12099     if (game.lenses_time_left == 0)
12100       RedrawAllInvisibleElementsForLenses();
12101   }
12102
12103   if (game.magnify_time_left > 0)
12104   {
12105     game.magnify_time_left--;
12106
12107     if (game.magnify_time_left == 0)
12108       RedrawAllInvisibleElementsForMagnifier();
12109   }
12110
12111   for (i = 0; i < MAX_PLAYERS; i++)
12112   {
12113     struct PlayerInfo *player = &stored_player[i];
12114
12115     if (SHIELD_ON(player))
12116     {
12117       if (player->shield_deadly_time_left)
12118         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12119       else if (player->shield_normal_time_left)
12120         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12121     }
12122   }
12123
12124 #if USE_DELAYED_GFX_REDRAW
12125   SCAN_PLAYFIELD(x, y)
12126   {
12127     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12128     {
12129       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12130          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12131
12132       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12133         DrawLevelField(x, y);
12134
12135       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12136         DrawLevelFieldCrumbled(x, y);
12137
12138       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12139         DrawLevelFieldCrumbledNeighbours(x, y);
12140
12141       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12142         DrawTwinkleOnField(x, y);
12143     }
12144
12145     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12146   }
12147 #endif
12148
12149   DrawAllPlayers();
12150   PlayAllPlayersSound();
12151
12152   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
12153   {
12154     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
12155
12156     local_player->show_envelope = 0;
12157   }
12158
12159   // use random number generator in every frame to make it less predictable
12160   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12161     RND(1);
12162 }
12163
12164 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12165 {
12166   int min_x = x, min_y = y, max_x = x, max_y = y;
12167   int i;
12168
12169   for (i = 0; i < MAX_PLAYERS; i++)
12170   {
12171     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12172
12173     if (!stored_player[i].active || &stored_player[i] == player)
12174       continue;
12175
12176     min_x = MIN(min_x, jx);
12177     min_y = MIN(min_y, jy);
12178     max_x = MAX(max_x, jx);
12179     max_y = MAX(max_y, jy);
12180   }
12181
12182   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12183 }
12184
12185 static boolean AllPlayersInVisibleScreen(void)
12186 {
12187   int i;
12188
12189   for (i = 0; i < MAX_PLAYERS; i++)
12190   {
12191     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12192
12193     if (!stored_player[i].active)
12194       continue;
12195
12196     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12197       return FALSE;
12198   }
12199
12200   return TRUE;
12201 }
12202
12203 void ScrollLevel(int dx, int dy)
12204 {
12205   int scroll_offset = 2 * TILEX_VAR;
12206   int x, y;
12207
12208   BlitBitmap(drawto_field, drawto_field,
12209              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12210              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12211              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12212              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12213              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12214              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12215
12216   if (dx != 0)
12217   {
12218     x = (dx == 1 ? BX1 : BX2);
12219     for (y = BY1; y <= BY2; y++)
12220       DrawScreenField(x, y);
12221   }
12222
12223   if (dy != 0)
12224   {
12225     y = (dy == 1 ? BY1 : BY2);
12226     for (x = BX1; x <= BX2; x++)
12227       DrawScreenField(x, y);
12228   }
12229
12230   redraw_mask |= REDRAW_FIELD;
12231 }
12232
12233 static boolean canFallDown(struct PlayerInfo *player)
12234 {
12235   int jx = player->jx, jy = player->jy;
12236
12237   return (IN_LEV_FIELD(jx, jy + 1) &&
12238           (IS_FREE(jx, jy + 1) ||
12239            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12240           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
12241           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
12242 }
12243
12244 static boolean canPassField(int x, int y, int move_dir)
12245 {
12246   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12247   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12248   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12249   int nextx = x + dx;
12250   int nexty = y + dy;
12251   int element = Feld[x][y];
12252
12253   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12254           !CAN_MOVE(element) &&
12255           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12256           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
12257           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12258 }
12259
12260 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12261 {
12262   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12263   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12264   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12265   int newx = x + dx;
12266   int newy = y + dy;
12267
12268   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12269           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12270           (IS_DIGGABLE(Feld[newx][newy]) ||
12271            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12272            canPassField(newx, newy, move_dir)));
12273 }
12274
12275 static void CheckGravityMovement(struct PlayerInfo *player)
12276 {
12277   if (player->gravity && !player->programmed_action)
12278   {
12279     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12280     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12281     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12282     int jx = player->jx, jy = player->jy;
12283     boolean player_is_moving_to_valid_field =
12284       (!player_is_snapping &&
12285        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12286         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12287     boolean player_can_fall_down = canFallDown(player);
12288
12289     if (player_can_fall_down &&
12290         !player_is_moving_to_valid_field)
12291       player->programmed_action = MV_DOWN;
12292   }
12293 }
12294
12295 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12296 {
12297   return CheckGravityMovement(player);
12298
12299   if (player->gravity && !player->programmed_action)
12300   {
12301     int jx = player->jx, jy = player->jy;
12302     boolean field_under_player_is_free =
12303       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12304     boolean player_is_standing_on_valid_field =
12305       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12306        (IS_WALKABLE(Feld[jx][jy]) &&
12307         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12308
12309     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12310       player->programmed_action = MV_DOWN;
12311   }
12312 }
12313
12314 /*
12315   MovePlayerOneStep()
12316   -----------------------------------------------------------------------------
12317   dx, dy:               direction (non-diagonal) to try to move the player to
12318   real_dx, real_dy:     direction as read from input device (can be diagonal)
12319 */
12320
12321 boolean MovePlayerOneStep(struct PlayerInfo *player,
12322                           int dx, int dy, int real_dx, int real_dy)
12323 {
12324   int jx = player->jx, jy = player->jy;
12325   int new_jx = jx + dx, new_jy = jy + dy;
12326   int can_move;
12327   boolean player_can_move = !player->cannot_move;
12328
12329   if (!player->active || (!dx && !dy))
12330     return MP_NO_ACTION;
12331
12332   player->MovDir = (dx < 0 ? MV_LEFT :
12333                     dx > 0 ? MV_RIGHT :
12334                     dy < 0 ? MV_UP :
12335                     dy > 0 ? MV_DOWN :  MV_NONE);
12336
12337   if (!IN_LEV_FIELD(new_jx, new_jy))
12338     return MP_NO_ACTION;
12339
12340   if (!player_can_move)
12341   {
12342     if (player->MovPos == 0)
12343     {
12344       player->is_moving = FALSE;
12345       player->is_digging = FALSE;
12346       player->is_collecting = FALSE;
12347       player->is_snapping = FALSE;
12348       player->is_pushing = FALSE;
12349     }
12350   }
12351
12352   if (!network.enabled && game.centered_player_nr == -1 &&
12353       !AllPlayersInSight(player, new_jx, new_jy))
12354     return MP_NO_ACTION;
12355
12356   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12357   if (can_move != MP_MOVING)
12358     return can_move;
12359
12360   // check if DigField() has caused relocation of the player
12361   if (player->jx != jx || player->jy != jy)
12362     return MP_NO_ACTION;        // <-- !!! CHECK THIS [-> MP_ACTION ?] !!!
12363
12364   StorePlayer[jx][jy] = 0;
12365   player->last_jx = jx;
12366   player->last_jy = jy;
12367   player->jx = new_jx;
12368   player->jy = new_jy;
12369   StorePlayer[new_jx][new_jy] = player->element_nr;
12370
12371   if (player->move_delay_value_next != -1)
12372   {
12373     player->move_delay_value = player->move_delay_value_next;
12374     player->move_delay_value_next = -1;
12375   }
12376
12377   player->MovPos =
12378     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12379
12380   player->step_counter++;
12381
12382   PlayerVisit[jx][jy] = FrameCounter;
12383
12384   player->is_moving = TRUE;
12385
12386 #if 1
12387   // should better be called in MovePlayer(), but this breaks some tapes
12388   ScrollPlayer(player, SCROLL_INIT);
12389 #endif
12390
12391   return MP_MOVING;
12392 }
12393
12394 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12395 {
12396   int jx = player->jx, jy = player->jy;
12397   int old_jx = jx, old_jy = jy;
12398   int moved = MP_NO_ACTION;
12399
12400   if (!player->active)
12401     return FALSE;
12402
12403   if (!dx && !dy)
12404   {
12405     if (player->MovPos == 0)
12406     {
12407       player->is_moving = FALSE;
12408       player->is_digging = FALSE;
12409       player->is_collecting = FALSE;
12410       player->is_snapping = FALSE;
12411       player->is_pushing = FALSE;
12412     }
12413
12414     return FALSE;
12415   }
12416
12417   if (player->move_delay > 0)
12418     return FALSE;
12419
12420   player->move_delay = -1;              // set to "uninitialized" value
12421
12422   // store if player is automatically moved to next field
12423   player->is_auto_moving = (player->programmed_action != MV_NONE);
12424
12425   // remove the last programmed player action
12426   player->programmed_action = 0;
12427
12428   if (player->MovPos)
12429   {
12430     // should only happen if pre-1.2 tape recordings are played
12431     // this is only for backward compatibility
12432
12433     int original_move_delay_value = player->move_delay_value;
12434
12435 #if DEBUG
12436     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
12437            tape.counter);
12438 #endif
12439
12440     // scroll remaining steps with finest movement resolution
12441     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12442
12443     while (player->MovPos)
12444     {
12445       ScrollPlayer(player, SCROLL_GO_ON);
12446       ScrollScreen(NULL, SCROLL_GO_ON);
12447
12448       AdvanceFrameAndPlayerCounters(player->index_nr);
12449
12450       DrawAllPlayers();
12451       BackToFront_WithFrameDelay(0);
12452     }
12453
12454     player->move_delay_value = original_move_delay_value;
12455   }
12456
12457   player->is_active = FALSE;
12458
12459   if (player->last_move_dir & MV_HORIZONTAL)
12460   {
12461     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12462       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12463   }
12464   else
12465   {
12466     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12467       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12468   }
12469
12470   if (!moved && !player->is_active)
12471   {
12472     player->is_moving = FALSE;
12473     player->is_digging = FALSE;
12474     player->is_collecting = FALSE;
12475     player->is_snapping = FALSE;
12476     player->is_pushing = FALSE;
12477   }
12478
12479   jx = player->jx;
12480   jy = player->jy;
12481
12482   if (moved & MP_MOVING && !ScreenMovPos &&
12483       (player->index_nr == game.centered_player_nr ||
12484        game.centered_player_nr == -1))
12485   {
12486     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12487     int offset = game.scroll_delay_value;
12488
12489     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12490     {
12491       // actual player has left the screen -- scroll in that direction
12492       if (jx != old_jx)         // player has moved horizontally
12493         scroll_x += (jx - old_jx);
12494       else                      // player has moved vertically
12495         scroll_y += (jy - old_jy);
12496     }
12497     else
12498     {
12499       if (jx != old_jx)         // player has moved horizontally
12500       {
12501         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
12502             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12503           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12504
12505         // don't scroll over playfield boundaries
12506         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12507           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12508
12509         // don't scroll more than one field at a time
12510         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12511
12512         // don't scroll against the player's moving direction
12513         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12514             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12515           scroll_x = old_scroll_x;
12516       }
12517       else                      // player has moved vertically
12518       {
12519         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
12520             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12521           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12522
12523         // don't scroll over playfield boundaries
12524         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12525           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12526
12527         // don't scroll more than one field at a time
12528         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12529
12530         // don't scroll against the player's moving direction
12531         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12532             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12533           scroll_y = old_scroll_y;
12534       }
12535     }
12536
12537     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12538     {
12539       if (!network.enabled && game.centered_player_nr == -1 &&
12540           !AllPlayersInVisibleScreen())
12541       {
12542         scroll_x = old_scroll_x;
12543         scroll_y = old_scroll_y;
12544       }
12545       else
12546       {
12547         ScrollScreen(player, SCROLL_INIT);
12548         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12549       }
12550     }
12551   }
12552
12553   player->StepFrame = 0;
12554
12555   if (moved & MP_MOVING)
12556   {
12557     if (old_jx != jx && old_jy == jy)
12558       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12559     else if (old_jx == jx && old_jy != jy)
12560       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12561
12562     TEST_DrawLevelField(jx, jy);        // for "crumbled sand"
12563
12564     player->last_move_dir = player->MovDir;
12565     player->is_moving = TRUE;
12566     player->is_snapping = FALSE;
12567     player->is_switching = FALSE;
12568     player->is_dropping = FALSE;
12569     player->is_dropping_pressed = FALSE;
12570     player->drop_pressed_delay = 0;
12571
12572 #if 0
12573     // should better be called here than above, but this breaks some tapes
12574     ScrollPlayer(player, SCROLL_INIT);
12575 #endif
12576   }
12577   else
12578   {
12579     CheckGravityMovementWhenNotMoving(player);
12580
12581     player->is_moving = FALSE;
12582
12583     /* at this point, the player is allowed to move, but cannot move right now
12584        (e.g. because of something blocking the way) -- ensure that the player
12585        is also allowed to move in the next frame (in old versions before 3.1.1,
12586        the player was forced to wait again for eight frames before next try) */
12587
12588     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12589       player->move_delay = 0;   // allow direct movement in the next frame
12590   }
12591
12592   if (player->move_delay == -1)         // not yet initialized by DigField()
12593     player->move_delay = player->move_delay_value;
12594
12595   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12596   {
12597     TestIfPlayerTouchesBadThing(jx, jy);
12598     TestIfPlayerTouchesCustomElement(jx, jy);
12599   }
12600
12601   if (!player->active)
12602     RemovePlayer(player);
12603
12604   return moved;
12605 }
12606
12607 void ScrollPlayer(struct PlayerInfo *player, int mode)
12608 {
12609   int jx = player->jx, jy = player->jy;
12610   int last_jx = player->last_jx, last_jy = player->last_jy;
12611   int move_stepsize = TILEX / player->move_delay_value;
12612
12613   if (!player->active)
12614     return;
12615
12616   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      // player not moving
12617     return;
12618
12619   if (mode == SCROLL_INIT)
12620   {
12621     player->actual_frame_counter = FrameCounter;
12622     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12623
12624     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12625         Feld[last_jx][last_jy] == EL_EMPTY)
12626     {
12627       int last_field_block_delay = 0;   // start with no blocking at all
12628       int block_delay_adjustment = player->block_delay_adjustment;
12629
12630       // if player blocks last field, add delay for exactly one move
12631       if (player->block_last_field)
12632       {
12633         last_field_block_delay += player->move_delay_value;
12634
12635         // when blocking enabled, prevent moving up despite gravity
12636         if (player->gravity && player->MovDir == MV_UP)
12637           block_delay_adjustment = -1;
12638       }
12639
12640       // add block delay adjustment (also possible when not blocking)
12641       last_field_block_delay += block_delay_adjustment;
12642
12643       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12644       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12645     }
12646
12647     if (player->MovPos != 0)    // player has not yet reached destination
12648       return;
12649   }
12650   else if (!FrameReached(&player->actual_frame_counter, 1))
12651     return;
12652
12653   if (player->MovPos != 0)
12654   {
12655     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12656     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12657
12658     // before DrawPlayer() to draw correct player graphic for this case
12659     if (player->MovPos == 0)
12660       CheckGravityMovement(player);
12661   }
12662
12663   if (player->MovPos == 0)      // player reached destination field
12664   {
12665     if (player->move_delay_reset_counter > 0)
12666     {
12667       player->move_delay_reset_counter--;
12668
12669       if (player->move_delay_reset_counter == 0)
12670       {
12671         // continue with normal speed after quickly moving through gate
12672         HALVE_PLAYER_SPEED(player);
12673
12674         // be able to make the next move without delay
12675         player->move_delay = 0;
12676       }
12677     }
12678
12679     player->last_jx = jx;
12680     player->last_jy = jy;
12681
12682     if (Feld[jx][jy] == EL_EXIT_OPEN ||
12683         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12684         Feld[jx][jy] == EL_EM_EXIT_OPENING ||
12685         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12686         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12687         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12688         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12689         Feld[jx][jy] == EL_SP_EXIT_OPENING)     // <-- special case
12690     {
12691       ExitPlayer(player);
12692
12693       if (game.players_still_needed == 0 &&
12694           (game.friends_still_needed == 0 ||
12695            IS_SP_ELEMENT(Feld[jx][jy])))
12696         LevelSolved();
12697     }
12698
12699     // this breaks one level: "machine", level 000
12700     {
12701       int move_direction = player->MovDir;
12702       int enter_side = MV_DIR_OPPOSITE(move_direction);
12703       int leave_side = move_direction;
12704       int old_jx = last_jx;
12705       int old_jy = last_jy;
12706       int old_element = Feld[old_jx][old_jy];
12707       int new_element = Feld[jx][jy];
12708
12709       if (IS_CUSTOM_ELEMENT(old_element))
12710         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12711                                    CE_LEFT_BY_PLAYER,
12712                                    player->index_bit, leave_side);
12713
12714       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12715                                           CE_PLAYER_LEAVES_X,
12716                                           player->index_bit, leave_side);
12717
12718       if (IS_CUSTOM_ELEMENT(new_element))
12719         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12720                                    player->index_bit, enter_side);
12721
12722       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12723                                           CE_PLAYER_ENTERS_X,
12724                                           player->index_bit, enter_side);
12725
12726       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12727                                         CE_MOVE_OF_X, move_direction);
12728     }
12729
12730     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12731     {
12732       TestIfPlayerTouchesBadThing(jx, jy);
12733       TestIfPlayerTouchesCustomElement(jx, jy);
12734
12735       /* needed because pushed element has not yet reached its destination,
12736          so it would trigger a change event at its previous field location */
12737       if (!player->is_pushing)
12738         TestIfElementTouchesCustomElement(jx, jy);      // for empty space
12739
12740       if (!player->active)
12741         RemovePlayer(player);
12742     }
12743
12744     if (!game.LevelSolved && level.use_step_counter)
12745     {
12746       int i;
12747
12748       TimePlayed++;
12749
12750       if (TimeLeft > 0)
12751       {
12752         TimeLeft--;
12753
12754         if (TimeLeft <= 10 && setup.time_limit)
12755           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12756
12757         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12758
12759         DisplayGameControlValues();
12760
12761         if (!TimeLeft && setup.time_limit)
12762           for (i = 0; i < MAX_PLAYERS; i++)
12763             KillPlayer(&stored_player[i]);
12764       }
12765       else if (game.no_time_limit && !game.all_players_gone)
12766       {
12767         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12768
12769         DisplayGameControlValues();
12770       }
12771     }
12772
12773     if (tape.single_step && tape.recording && !tape.pausing &&
12774         !player->programmed_action)
12775       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12776
12777     if (!player->programmed_action)
12778       CheckSaveEngineSnapshot(player);
12779   }
12780 }
12781
12782 void ScrollScreen(struct PlayerInfo *player, int mode)
12783 {
12784   static unsigned int screen_frame_counter = 0;
12785
12786   if (mode == SCROLL_INIT)
12787   {
12788     // set scrolling step size according to actual player's moving speed
12789     ScrollStepSize = TILEX / player->move_delay_value;
12790
12791     screen_frame_counter = FrameCounter;
12792     ScreenMovDir = player->MovDir;
12793     ScreenMovPos = player->MovPos;
12794     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12795     return;
12796   }
12797   else if (!FrameReached(&screen_frame_counter, 1))
12798     return;
12799
12800   if (ScreenMovPos)
12801   {
12802     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12803     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12804     redraw_mask |= REDRAW_FIELD;
12805   }
12806   else
12807     ScreenMovDir = MV_NONE;
12808 }
12809
12810 void TestIfPlayerTouchesCustomElement(int x, int y)
12811 {
12812   static int xy[4][2] =
12813   {
12814     { 0, -1 },
12815     { -1, 0 },
12816     { +1, 0 },
12817     { 0, +1 }
12818   };
12819   static int trigger_sides[4][2] =
12820   {
12821     // center side       border side
12822     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
12823     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
12824     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
12825     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
12826   };
12827   static int touch_dir[4] =
12828   {
12829     MV_LEFT | MV_RIGHT,
12830     MV_UP   | MV_DOWN,
12831     MV_UP   | MV_DOWN,
12832     MV_LEFT | MV_RIGHT
12833   };
12834   int center_element = Feld[x][y];      // should always be non-moving!
12835   int i;
12836
12837   for (i = 0; i < NUM_DIRECTIONS; i++)
12838   {
12839     int xx = x + xy[i][0];
12840     int yy = y + xy[i][1];
12841     int center_side = trigger_sides[i][0];
12842     int border_side = trigger_sides[i][1];
12843     int border_element;
12844
12845     if (!IN_LEV_FIELD(xx, yy))
12846       continue;
12847
12848     if (IS_PLAYER(x, y))                // player found at center element
12849     {
12850       struct PlayerInfo *player = PLAYERINFO(x, y);
12851
12852       if (game.engine_version < VERSION_IDENT(3,0,7,0))
12853         border_element = Feld[xx][yy];          // may be moving!
12854       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12855         border_element = Feld[xx][yy];
12856       else if (MovDir[xx][yy] & touch_dir[i])   // elements are touching
12857         border_element = MovingOrBlocked2Element(xx, yy);
12858       else
12859         continue;               // center and border element do not touch
12860
12861       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12862                                  player->index_bit, border_side);
12863       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12864                                           CE_PLAYER_TOUCHES_X,
12865                                           player->index_bit, border_side);
12866
12867       {
12868         /* use player element that is initially defined in the level playfield,
12869            not the player element that corresponds to the runtime player number
12870            (example: a level that contains EL_PLAYER_3 as the only player would
12871            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12872         int player_element = PLAYERINFO(x, y)->initial_element;
12873
12874         CheckElementChangeBySide(xx, yy, border_element, player_element,
12875                                  CE_TOUCHING_X, border_side);
12876       }
12877     }
12878     else if (IS_PLAYER(xx, yy))         // player found at border element
12879     {
12880       struct PlayerInfo *player = PLAYERINFO(xx, yy);
12881
12882       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12883       {
12884         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12885           continue;             // center and border element do not touch
12886       }
12887
12888       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12889                                  player->index_bit, center_side);
12890       CheckTriggeredElementChangeByPlayer(x, y, center_element,
12891                                           CE_PLAYER_TOUCHES_X,
12892                                           player->index_bit, center_side);
12893
12894       {
12895         /* use player element that is initially defined in the level playfield,
12896            not the player element that corresponds to the runtime player number
12897            (example: a level that contains EL_PLAYER_3 as the only player would
12898            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12899         int player_element = PLAYERINFO(xx, yy)->initial_element;
12900
12901         CheckElementChangeBySide(x, y, center_element, player_element,
12902                                  CE_TOUCHING_X, center_side);
12903       }
12904
12905       break;
12906     }
12907   }
12908 }
12909
12910 void TestIfElementTouchesCustomElement(int x, int y)
12911 {
12912   static int xy[4][2] =
12913   {
12914     { 0, -1 },
12915     { -1, 0 },
12916     { +1, 0 },
12917     { 0, +1 }
12918   };
12919   static int trigger_sides[4][2] =
12920   {
12921     // center side      border side
12922     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
12923     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
12924     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
12925     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
12926   };
12927   static int touch_dir[4] =
12928   {
12929     MV_LEFT | MV_RIGHT,
12930     MV_UP   | MV_DOWN,
12931     MV_UP   | MV_DOWN,
12932     MV_LEFT | MV_RIGHT
12933   };
12934   boolean change_center_element = FALSE;
12935   int center_element = Feld[x][y];      // should always be non-moving!
12936   int border_element_old[NUM_DIRECTIONS];
12937   int i;
12938
12939   for (i = 0; i < NUM_DIRECTIONS; i++)
12940   {
12941     int xx = x + xy[i][0];
12942     int yy = y + xy[i][1];
12943     int border_element;
12944
12945     border_element_old[i] = -1;
12946
12947     if (!IN_LEV_FIELD(xx, yy))
12948       continue;
12949
12950     if (game.engine_version < VERSION_IDENT(3,0,7,0))
12951       border_element = Feld[xx][yy];    // may be moving!
12952     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12953       border_element = Feld[xx][yy];
12954     else if (MovDir[xx][yy] & touch_dir[i])     // elements are touching
12955       border_element = MovingOrBlocked2Element(xx, yy);
12956     else
12957       continue;                 // center and border element do not touch
12958
12959     border_element_old[i] = border_element;
12960   }
12961
12962   for (i = 0; i < NUM_DIRECTIONS; i++)
12963   {
12964     int xx = x + xy[i][0];
12965     int yy = y + xy[i][1];
12966     int center_side = trigger_sides[i][0];
12967     int border_element = border_element_old[i];
12968
12969     if (border_element == -1)
12970       continue;
12971
12972     // check for change of border element
12973     CheckElementChangeBySide(xx, yy, border_element, center_element,
12974                              CE_TOUCHING_X, center_side);
12975
12976     // (center element cannot be player, so we dont have to check this here)
12977   }
12978
12979   for (i = 0; i < NUM_DIRECTIONS; i++)
12980   {
12981     int xx = x + xy[i][0];
12982     int yy = y + xy[i][1];
12983     int border_side = trigger_sides[i][1];
12984     int border_element = border_element_old[i];
12985
12986     if (border_element == -1)
12987       continue;
12988
12989     // check for change of center element (but change it only once)
12990     if (!change_center_element)
12991       change_center_element =
12992         CheckElementChangeBySide(x, y, center_element, border_element,
12993                                  CE_TOUCHING_X, border_side);
12994
12995     if (IS_PLAYER(xx, yy))
12996     {
12997       /* use player element that is initially defined in the level playfield,
12998          not the player element that corresponds to the runtime player number
12999          (example: a level that contains EL_PLAYER_3 as the only player would
13000          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13001       int player_element = PLAYERINFO(xx, yy)->initial_element;
13002
13003       CheckElementChangeBySide(x, y, center_element, player_element,
13004                                CE_TOUCHING_X, border_side);
13005     }
13006   }
13007 }
13008
13009 void TestIfElementHitsCustomElement(int x, int y, int direction)
13010 {
13011   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13012   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13013   int hitx = x + dx, hity = y + dy;
13014   int hitting_element = Feld[x][y];
13015   int touched_element;
13016
13017   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13018     return;
13019
13020   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13021                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13022
13023   if (IN_LEV_FIELD(hitx, hity))
13024   {
13025     int opposite_direction = MV_DIR_OPPOSITE(direction);
13026     int hitting_side = direction;
13027     int touched_side = opposite_direction;
13028     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13029                           MovDir[hitx][hity] != direction ||
13030                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13031
13032     object_hit = TRUE;
13033
13034     if (object_hit)
13035     {
13036       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13037                                CE_HITTING_X, touched_side);
13038
13039       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13040                                CE_HIT_BY_X, hitting_side);
13041
13042       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13043                                CE_HIT_BY_SOMETHING, opposite_direction);
13044
13045       if (IS_PLAYER(hitx, hity))
13046       {
13047         /* use player element that is initially defined in the level playfield,
13048            not the player element that corresponds to the runtime player number
13049            (example: a level that contains EL_PLAYER_3 as the only player would
13050            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13051         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13052
13053         CheckElementChangeBySide(x, y, hitting_element, player_element,
13054                                  CE_HITTING_X, touched_side);
13055       }
13056     }
13057   }
13058
13059   // "hitting something" is also true when hitting the playfield border
13060   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13061                            CE_HITTING_SOMETHING, direction);
13062 }
13063
13064 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13065 {
13066   int i, kill_x = -1, kill_y = -1;
13067
13068   int bad_element = -1;
13069   static int test_xy[4][2] =
13070   {
13071     { 0, -1 },
13072     { -1, 0 },
13073     { +1, 0 },
13074     { 0, +1 }
13075   };
13076   static int test_dir[4] =
13077   {
13078     MV_UP,
13079     MV_LEFT,
13080     MV_RIGHT,
13081     MV_DOWN
13082   };
13083
13084   for (i = 0; i < NUM_DIRECTIONS; i++)
13085   {
13086     int test_x, test_y, test_move_dir, test_element;
13087
13088     test_x = good_x + test_xy[i][0];
13089     test_y = good_y + test_xy[i][1];
13090
13091     if (!IN_LEV_FIELD(test_x, test_y))
13092       continue;
13093
13094     test_move_dir =
13095       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13096
13097     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13098
13099     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13100        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13101     */
13102     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13103         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13104     {
13105       kill_x = test_x;
13106       kill_y = test_y;
13107       bad_element = test_element;
13108
13109       break;
13110     }
13111   }
13112
13113   if (kill_x != -1 || kill_y != -1)
13114   {
13115     if (IS_PLAYER(good_x, good_y))
13116     {
13117       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13118
13119       if (player->shield_deadly_time_left > 0 &&
13120           !IS_INDESTRUCTIBLE(bad_element))
13121         Bang(kill_x, kill_y);
13122       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13123         KillPlayer(player);
13124     }
13125     else
13126       Bang(good_x, good_y);
13127   }
13128 }
13129
13130 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13131 {
13132   int i, kill_x = -1, kill_y = -1;
13133   int bad_element = Feld[bad_x][bad_y];
13134   static int test_xy[4][2] =
13135   {
13136     { 0, -1 },
13137     { -1, 0 },
13138     { +1, 0 },
13139     { 0, +1 }
13140   };
13141   static int touch_dir[4] =
13142   {
13143     MV_LEFT | MV_RIGHT,
13144     MV_UP   | MV_DOWN,
13145     MV_UP   | MV_DOWN,
13146     MV_LEFT | MV_RIGHT
13147   };
13148   static int test_dir[4] =
13149   {
13150     MV_UP,
13151     MV_LEFT,
13152     MV_RIGHT,
13153     MV_DOWN
13154   };
13155
13156   if (bad_element == EL_EXPLOSION)      // skip just exploding bad things
13157     return;
13158
13159   for (i = 0; i < NUM_DIRECTIONS; i++)
13160   {
13161     int test_x, test_y, test_move_dir, test_element;
13162
13163     test_x = bad_x + test_xy[i][0];
13164     test_y = bad_y + test_xy[i][1];
13165
13166     if (!IN_LEV_FIELD(test_x, test_y))
13167       continue;
13168
13169     test_move_dir =
13170       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13171
13172     test_element = Feld[test_x][test_y];
13173
13174     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13175        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13176     */
13177     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13178         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13179     {
13180       // good thing is player or penguin that does not move away
13181       if (IS_PLAYER(test_x, test_y))
13182       {
13183         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13184
13185         if (bad_element == EL_ROBOT && player->is_moving)
13186           continue;     // robot does not kill player if he is moving
13187
13188         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13189         {
13190           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13191             continue;           // center and border element do not touch
13192         }
13193
13194         kill_x = test_x;
13195         kill_y = test_y;
13196
13197         break;
13198       }
13199       else if (test_element == EL_PENGUIN)
13200       {
13201         kill_x = test_x;
13202         kill_y = test_y;
13203
13204         break;
13205       }
13206     }
13207   }
13208
13209   if (kill_x != -1 || kill_y != -1)
13210   {
13211     if (IS_PLAYER(kill_x, kill_y))
13212     {
13213       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13214
13215       if (player->shield_deadly_time_left > 0 &&
13216           !IS_INDESTRUCTIBLE(bad_element))
13217         Bang(bad_x, bad_y);
13218       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13219         KillPlayer(player);
13220     }
13221     else
13222       Bang(kill_x, kill_y);
13223   }
13224 }
13225
13226 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13227 {
13228   int bad_element = Feld[bad_x][bad_y];
13229   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13230   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13231   int test_x = bad_x + dx, test_y = bad_y + dy;
13232   int test_move_dir, test_element;
13233   int kill_x = -1, kill_y = -1;
13234
13235   if (!IN_LEV_FIELD(test_x, test_y))
13236     return;
13237
13238   test_move_dir =
13239     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13240
13241   test_element = Feld[test_x][test_y];
13242
13243   if (test_move_dir != bad_move_dir)
13244   {
13245     // good thing can be player or penguin that does not move away
13246     if (IS_PLAYER(test_x, test_y))
13247     {
13248       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13249
13250       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13251          player as being hit when he is moving towards the bad thing, because
13252          the "get hit by" condition would be lost after the player stops) */
13253       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13254         return;         // player moves away from bad thing
13255
13256       kill_x = test_x;
13257       kill_y = test_y;
13258     }
13259     else if (test_element == EL_PENGUIN)
13260     {
13261       kill_x = test_x;
13262       kill_y = test_y;
13263     }
13264   }
13265
13266   if (kill_x != -1 || kill_y != -1)
13267   {
13268     if (IS_PLAYER(kill_x, kill_y))
13269     {
13270       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13271
13272       if (player->shield_deadly_time_left > 0 &&
13273           !IS_INDESTRUCTIBLE(bad_element))
13274         Bang(bad_x, bad_y);
13275       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13276         KillPlayer(player);
13277     }
13278     else
13279       Bang(kill_x, kill_y);
13280   }
13281 }
13282
13283 void TestIfPlayerTouchesBadThing(int x, int y)
13284 {
13285   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13286 }
13287
13288 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13289 {
13290   TestIfGoodThingHitsBadThing(x, y, move_dir);
13291 }
13292
13293 void TestIfBadThingTouchesPlayer(int x, int y)
13294 {
13295   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13296 }
13297
13298 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13299 {
13300   TestIfBadThingHitsGoodThing(x, y, move_dir);
13301 }
13302
13303 void TestIfFriendTouchesBadThing(int x, int y)
13304 {
13305   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13306 }
13307
13308 void TestIfBadThingTouchesFriend(int x, int y)
13309 {
13310   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13311 }
13312
13313 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13314 {
13315   int i, kill_x = bad_x, kill_y = bad_y;
13316   static int xy[4][2] =
13317   {
13318     { 0, -1 },
13319     { -1, 0 },
13320     { +1, 0 },
13321     { 0, +1 }
13322   };
13323
13324   for (i = 0; i < NUM_DIRECTIONS; i++)
13325   {
13326     int x, y, element;
13327
13328     x = bad_x + xy[i][0];
13329     y = bad_y + xy[i][1];
13330     if (!IN_LEV_FIELD(x, y))
13331       continue;
13332
13333     element = Feld[x][y];
13334     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13335         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13336     {
13337       kill_x = x;
13338       kill_y = y;
13339       break;
13340     }
13341   }
13342
13343   if (kill_x != bad_x || kill_y != bad_y)
13344     Bang(bad_x, bad_y);
13345 }
13346
13347 void KillPlayer(struct PlayerInfo *player)
13348 {
13349   int jx = player->jx, jy = player->jy;
13350
13351   if (!player->active)
13352     return;
13353
13354 #if 0
13355   printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
13356          player->killed, player->active, player->reanimated);
13357 #endif
13358
13359   /* the following code was introduced to prevent an infinite loop when calling
13360      -> Bang()
13361      -> CheckTriggeredElementChangeExt()
13362      -> ExecuteCustomElementAction()
13363      -> KillPlayer()
13364      -> (infinitely repeating the above sequence of function calls)
13365      which occurs when killing the player while having a CE with the setting
13366      "kill player X when explosion of <player X>"; the solution using a new
13367      field "player->killed" was chosen for backwards compatibility, although
13368      clever use of the fields "player->active" etc. would probably also work */
13369 #if 1
13370   if (player->killed)
13371     return;
13372 #endif
13373
13374   player->killed = TRUE;
13375
13376   // remove accessible field at the player's position
13377   Feld[jx][jy] = EL_EMPTY;
13378
13379   // deactivate shield (else Bang()/Explode() would not work right)
13380   player->shield_normal_time_left = 0;
13381   player->shield_deadly_time_left = 0;
13382
13383 #if 0
13384   printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
13385          player->killed, player->active, player->reanimated);
13386 #endif
13387
13388   Bang(jx, jy);
13389
13390 #if 0
13391   printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
13392          player->killed, player->active, player->reanimated);
13393 #endif
13394
13395   if (player->reanimated)       // killed player may have been reanimated
13396     player->killed = player->reanimated = FALSE;
13397   else
13398     BuryPlayer(player);
13399 }
13400
13401 static void KillPlayerUnlessEnemyProtected(int x, int y)
13402 {
13403   if (!PLAYER_ENEMY_PROTECTED(x, y))
13404     KillPlayer(PLAYERINFO(x, y));
13405 }
13406
13407 static void KillPlayerUnlessExplosionProtected(int x, int y)
13408 {
13409   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13410     KillPlayer(PLAYERINFO(x, y));
13411 }
13412
13413 void BuryPlayer(struct PlayerInfo *player)
13414 {
13415   int jx = player->jx, jy = player->jy;
13416
13417   if (!player->active)
13418     return;
13419
13420   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13421   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13422
13423   RemovePlayer(player);
13424
13425   player->buried = TRUE;
13426
13427   if (game.all_players_gone)
13428     game.GameOver = TRUE;
13429 }
13430
13431 void RemovePlayer(struct PlayerInfo *player)
13432 {
13433   int jx = player->jx, jy = player->jy;
13434   int i, found = FALSE;
13435
13436   player->present = FALSE;
13437   player->active = FALSE;
13438
13439   if (!ExplodeField[jx][jy])
13440     StorePlayer[jx][jy] = 0;
13441
13442   if (player->is_moving)
13443     TEST_DrawLevelField(player->last_jx, player->last_jy);
13444
13445   for (i = 0; i < MAX_PLAYERS; i++)
13446     if (stored_player[i].active)
13447       found = TRUE;
13448
13449   if (!found)
13450   {
13451     game.all_players_gone = TRUE;
13452     game.GameOver = TRUE;
13453   }
13454
13455   game.exit_x = game.robot_wheel_x = jx;
13456   game.exit_y = game.robot_wheel_y = jy;
13457 }
13458
13459 void ExitPlayer(struct PlayerInfo *player)
13460 {
13461   DrawPlayer(player);   // needed here only to cleanup last field
13462   RemovePlayer(player);
13463
13464   if (game.players_still_needed > 0)
13465     game.players_still_needed--;
13466 }
13467
13468 static void setFieldForSnapping(int x, int y, int element, int direction)
13469 {
13470   struct ElementInfo *ei = &element_info[element];
13471   int direction_bit = MV_DIR_TO_BIT(direction);
13472   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13473   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13474                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13475
13476   Feld[x][y] = EL_ELEMENT_SNAPPING;
13477   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13478
13479   ResetGfxAnimation(x, y);
13480
13481   GfxElement[x][y] = element;
13482   GfxAction[x][y] = action;
13483   GfxDir[x][y] = direction;
13484   GfxFrame[x][y] = -1;
13485 }
13486
13487 /*
13488   =============================================================================
13489   checkDiagonalPushing()
13490   -----------------------------------------------------------------------------
13491   check if diagonal input device direction results in pushing of object
13492   (by checking if the alternative direction is walkable, diggable, ...)
13493   =============================================================================
13494 */
13495
13496 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13497                                     int x, int y, int real_dx, int real_dy)
13498 {
13499   int jx, jy, dx, dy, xx, yy;
13500
13501   if (real_dx == 0 || real_dy == 0)     // no diagonal direction => push
13502     return TRUE;
13503
13504   // diagonal direction: check alternative direction
13505   jx = player->jx;
13506   jy = player->jy;
13507   dx = x - jx;
13508   dy = y - jy;
13509   xx = jx + (dx == 0 ? real_dx : 0);
13510   yy = jy + (dy == 0 ? real_dy : 0);
13511
13512   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13513 }
13514
13515 /*
13516   =============================================================================
13517   DigField()
13518   -----------------------------------------------------------------------------
13519   x, y:                 field next to player (non-diagonal) to try to dig to
13520   real_dx, real_dy:     direction as read from input device (can be diagonal)
13521   =============================================================================
13522 */
13523
13524 static int DigField(struct PlayerInfo *player,
13525                     int oldx, int oldy, int x, int y,
13526                     int real_dx, int real_dy, int mode)
13527 {
13528   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13529   boolean player_was_pushing = player->is_pushing;
13530   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13531   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13532   int jx = oldx, jy = oldy;
13533   int dx = x - jx, dy = y - jy;
13534   int nextx = x + dx, nexty = y + dy;
13535   int move_direction = (dx == -1 ? MV_LEFT  :
13536                         dx == +1 ? MV_RIGHT :
13537                         dy == -1 ? MV_UP    :
13538                         dy == +1 ? MV_DOWN  : MV_NONE);
13539   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13540   int dig_side = MV_DIR_OPPOSITE(move_direction);
13541   int old_element = Feld[jx][jy];
13542   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13543   int collect_count;
13544
13545   if (is_player)                // function can also be called by EL_PENGUIN
13546   {
13547     if (player->MovPos == 0)
13548     {
13549       player->is_digging = FALSE;
13550       player->is_collecting = FALSE;
13551     }
13552
13553     if (player->MovPos == 0)    // last pushing move finished
13554       player->is_pushing = FALSE;
13555
13556     if (mode == DF_NO_PUSH)     // player just stopped pushing
13557     {
13558       player->is_switching = FALSE;
13559       player->push_delay = -1;
13560
13561       return MP_NO_ACTION;
13562     }
13563   }
13564
13565   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13566     old_element = Back[jx][jy];
13567
13568   // in case of element dropped at player position, check background
13569   else if (Back[jx][jy] != EL_EMPTY &&
13570            game.engine_version >= VERSION_IDENT(2,2,0,0))
13571     old_element = Back[jx][jy];
13572
13573   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13574     return MP_NO_ACTION;        // field has no opening in this direction
13575
13576   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13577     return MP_NO_ACTION;        // field has no opening in this direction
13578
13579   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13580   {
13581     SplashAcid(x, y);
13582
13583     Feld[jx][jy] = player->artwork_element;
13584     InitMovingField(jx, jy, MV_DOWN);
13585     Store[jx][jy] = EL_ACID;
13586     ContinueMoving(jx, jy);
13587     BuryPlayer(player);
13588
13589     return MP_DONT_RUN_INTO;
13590   }
13591
13592   if (player_can_move && DONT_RUN_INTO(element))
13593   {
13594     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13595
13596     return MP_DONT_RUN_INTO;
13597   }
13598
13599   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13600     return MP_NO_ACTION;
13601
13602   collect_count = element_info[element].collect_count_initial;
13603
13604   if (!is_player && !IS_COLLECTIBLE(element))   // penguin cannot collect it
13605     return MP_NO_ACTION;
13606
13607   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13608     player_can_move = player_can_move_or_snap;
13609
13610   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13611       game.engine_version >= VERSION_IDENT(2,2,0,0))
13612   {
13613     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13614                                player->index_bit, dig_side);
13615     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13616                                         player->index_bit, dig_side);
13617
13618     if (element == EL_DC_LANDMINE)
13619       Bang(x, y);
13620
13621     if (Feld[x][y] != element)          // field changed by snapping
13622       return MP_ACTION;
13623
13624     return MP_NO_ACTION;
13625   }
13626
13627   if (player->gravity && is_player && !player->is_auto_moving &&
13628       canFallDown(player) && move_direction != MV_DOWN &&
13629       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13630     return MP_NO_ACTION;        // player cannot walk here due to gravity
13631
13632   if (player_can_move &&
13633       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13634   {
13635     int sound_element = SND_ELEMENT(element);
13636     int sound_action = ACTION_WALKING;
13637
13638     if (IS_RND_GATE(element))
13639     {
13640       if (!player->key[RND_GATE_NR(element)])
13641         return MP_NO_ACTION;
13642     }
13643     else if (IS_RND_GATE_GRAY(element))
13644     {
13645       if (!player->key[RND_GATE_GRAY_NR(element)])
13646         return MP_NO_ACTION;
13647     }
13648     else if (IS_RND_GATE_GRAY_ACTIVE(element))
13649     {
13650       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13651         return MP_NO_ACTION;
13652     }
13653     else if (element == EL_EXIT_OPEN ||
13654              element == EL_EM_EXIT_OPEN ||
13655              element == EL_EM_EXIT_OPENING ||
13656              element == EL_STEEL_EXIT_OPEN ||
13657              element == EL_EM_STEEL_EXIT_OPEN ||
13658              element == EL_EM_STEEL_EXIT_OPENING ||
13659              element == EL_SP_EXIT_OPEN ||
13660              element == EL_SP_EXIT_OPENING)
13661     {
13662       sound_action = ACTION_PASSING;    // player is passing exit
13663     }
13664     else if (element == EL_EMPTY)
13665     {
13666       sound_action = ACTION_MOVING;             // nothing to walk on
13667     }
13668
13669     // play sound from background or player, whatever is available
13670     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13671       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13672     else
13673       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13674   }
13675   else if (player_can_move &&
13676            IS_PASSABLE(element) && canPassField(x, y, move_direction))
13677   {
13678     if (!ACCESS_FROM(element, opposite_direction))
13679       return MP_NO_ACTION;      // field not accessible from this direction
13680
13681     if (CAN_MOVE(element))      // only fixed elements can be passed!
13682       return MP_NO_ACTION;
13683
13684     if (IS_EM_GATE(element))
13685     {
13686       if (!player->key[EM_GATE_NR(element)])
13687         return MP_NO_ACTION;
13688     }
13689     else if (IS_EM_GATE_GRAY(element))
13690     {
13691       if (!player->key[EM_GATE_GRAY_NR(element)])
13692         return MP_NO_ACTION;
13693     }
13694     else if (IS_EM_GATE_GRAY_ACTIVE(element))
13695     {
13696       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13697         return MP_NO_ACTION;
13698     }
13699     else if (IS_EMC_GATE(element))
13700     {
13701       if (!player->key[EMC_GATE_NR(element)])
13702         return MP_NO_ACTION;
13703     }
13704     else if (IS_EMC_GATE_GRAY(element))
13705     {
13706       if (!player->key[EMC_GATE_GRAY_NR(element)])
13707         return MP_NO_ACTION;
13708     }
13709     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13710     {
13711       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13712         return MP_NO_ACTION;
13713     }
13714     else if (element == EL_DC_GATE_WHITE ||
13715              element == EL_DC_GATE_WHITE_GRAY ||
13716              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13717     {
13718       if (player->num_white_keys == 0)
13719         return MP_NO_ACTION;
13720
13721       player->num_white_keys--;
13722     }
13723     else if (IS_SP_PORT(element))
13724     {
13725       if (element == EL_SP_GRAVITY_PORT_LEFT ||
13726           element == EL_SP_GRAVITY_PORT_RIGHT ||
13727           element == EL_SP_GRAVITY_PORT_UP ||
13728           element == EL_SP_GRAVITY_PORT_DOWN)
13729         player->gravity = !player->gravity;
13730       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13731                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13732                element == EL_SP_GRAVITY_ON_PORT_UP ||
13733                element == EL_SP_GRAVITY_ON_PORT_DOWN)
13734         player->gravity = TRUE;
13735       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13736                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13737                element == EL_SP_GRAVITY_OFF_PORT_UP ||
13738                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13739         player->gravity = FALSE;
13740     }
13741
13742     // automatically move to the next field with double speed
13743     player->programmed_action = move_direction;
13744
13745     if (player->move_delay_reset_counter == 0)
13746     {
13747       player->move_delay_reset_counter = 2;     // two double speed steps
13748
13749       DOUBLE_PLAYER_SPEED(player);
13750     }
13751
13752     PlayLevelSoundAction(x, y, ACTION_PASSING);
13753   }
13754   else if (player_can_move_or_snap && IS_DIGGABLE(element))
13755   {
13756     RemoveField(x, y);
13757
13758     if (mode != DF_SNAP)
13759     {
13760       GfxElement[x][y] = GFX_ELEMENT(element);
13761       player->is_digging = TRUE;
13762     }
13763
13764     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13765
13766     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13767                                         player->index_bit, dig_side);
13768
13769     if (mode == DF_SNAP)
13770     {
13771       if (level.block_snap_field)
13772         setFieldForSnapping(x, y, element, move_direction);
13773       else
13774         TestIfElementTouchesCustomElement(x, y);        // for empty space
13775
13776       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13777                                           player->index_bit, dig_side);
13778     }
13779   }
13780   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13781   {
13782     RemoveField(x, y);
13783
13784     if (is_player && mode != DF_SNAP)
13785     {
13786       GfxElement[x][y] = element;
13787       player->is_collecting = TRUE;
13788     }
13789
13790     if (element == EL_SPEED_PILL)
13791     {
13792       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13793     }
13794     else if (element == EL_EXTRA_TIME && level.time > 0)
13795     {
13796       TimeLeft += level.extra_time;
13797
13798       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13799
13800       DisplayGameControlValues();
13801     }
13802     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13803     {
13804       player->shield_normal_time_left += level.shield_normal_time;
13805       if (element == EL_SHIELD_DEADLY)
13806         player->shield_deadly_time_left += level.shield_deadly_time;
13807     }
13808     else if (element == EL_DYNAMITE ||
13809              element == EL_EM_DYNAMITE ||
13810              element == EL_SP_DISK_RED)
13811     {
13812       if (player->inventory_size < MAX_INVENTORY_SIZE)
13813         player->inventory_element[player->inventory_size++] = element;
13814
13815       DrawGameDoorValues();
13816     }
13817     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13818     {
13819       player->dynabomb_count++;
13820       player->dynabombs_left++;
13821     }
13822     else if (element == EL_DYNABOMB_INCREASE_SIZE)
13823     {
13824       player->dynabomb_size++;
13825     }
13826     else if (element == EL_DYNABOMB_INCREASE_POWER)
13827     {
13828       player->dynabomb_xl = TRUE;
13829     }
13830     else if (IS_KEY(element))
13831     {
13832       player->key[KEY_NR(element)] = TRUE;
13833
13834       DrawGameDoorValues();
13835     }
13836     else if (element == EL_DC_KEY_WHITE)
13837     {
13838       player->num_white_keys++;
13839
13840       // display white keys?
13841       // DrawGameDoorValues();
13842     }
13843     else if (IS_ENVELOPE(element))
13844     {
13845       player->show_envelope = element;
13846     }
13847     else if (element == EL_EMC_LENSES)
13848     {
13849       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13850
13851       RedrawAllInvisibleElementsForLenses();
13852     }
13853     else if (element == EL_EMC_MAGNIFIER)
13854     {
13855       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13856
13857       RedrawAllInvisibleElementsForMagnifier();
13858     }
13859     else if (IS_DROPPABLE(element) ||
13860              IS_THROWABLE(element))     // can be collected and dropped
13861     {
13862       int i;
13863
13864       if (collect_count == 0)
13865         player->inventory_infinite_element = element;
13866       else
13867         for (i = 0; i < collect_count; i++)
13868           if (player->inventory_size < MAX_INVENTORY_SIZE)
13869             player->inventory_element[player->inventory_size++] = element;
13870
13871       DrawGameDoorValues();
13872     }
13873     else if (collect_count > 0)
13874     {
13875       game.gems_still_needed -= collect_count;
13876       if (game.gems_still_needed < 0)
13877         game.gems_still_needed = 0;
13878
13879       game.snapshot.collected_item = TRUE;
13880
13881       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
13882
13883       DisplayGameControlValues();
13884     }
13885
13886     RaiseScoreElement(element);
13887     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13888
13889     if (is_player)
13890       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13891                                           player->index_bit, dig_side);
13892
13893     if (mode == DF_SNAP)
13894     {
13895       if (level.block_snap_field)
13896         setFieldForSnapping(x, y, element, move_direction);
13897       else
13898         TestIfElementTouchesCustomElement(x, y);        // for empty space
13899
13900       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13901                                           player->index_bit, dig_side);
13902     }
13903   }
13904   else if (player_can_move_or_snap && IS_PUSHABLE(element))
13905   {
13906     if (mode == DF_SNAP && element != EL_BD_ROCK)
13907       return MP_NO_ACTION;
13908
13909     if (CAN_FALL(element) && dy)
13910       return MP_NO_ACTION;
13911
13912     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13913         !(element == EL_SPRING && level.use_spring_bug))
13914       return MP_NO_ACTION;
13915
13916     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13917         ((move_direction & MV_VERTICAL &&
13918           ((element_info[element].move_pattern & MV_LEFT &&
13919             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13920            (element_info[element].move_pattern & MV_RIGHT &&
13921             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13922          (move_direction & MV_HORIZONTAL &&
13923           ((element_info[element].move_pattern & MV_UP &&
13924             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13925            (element_info[element].move_pattern & MV_DOWN &&
13926             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13927       return MP_NO_ACTION;
13928
13929     // do not push elements already moving away faster than player
13930     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13931         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13932       return MP_NO_ACTION;
13933
13934     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13935     {
13936       if (player->push_delay_value == -1 || !player_was_pushing)
13937         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13938     }
13939     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13940     {
13941       if (player->push_delay_value == -1)
13942         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13943     }
13944     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13945     {
13946       if (!player->is_pushing)
13947         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13948     }
13949
13950     player->is_pushing = TRUE;
13951     player->is_active = TRUE;
13952
13953     if (!(IN_LEV_FIELD(nextx, nexty) &&
13954           (IS_FREE(nextx, nexty) ||
13955            (IS_SB_ELEMENT(element) &&
13956             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
13957            (IS_CUSTOM_ELEMENT(element) &&
13958             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
13959       return MP_NO_ACTION;
13960
13961     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13962       return MP_NO_ACTION;
13963
13964     if (player->push_delay == -1)       // new pushing; restart delay
13965       player->push_delay = 0;
13966
13967     if (player->push_delay < player->push_delay_value &&
13968         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13969         element != EL_SPRING && element != EL_BALLOON)
13970     {
13971       // make sure that there is no move delay before next try to push
13972       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13973         player->move_delay = 0;
13974
13975       return MP_NO_ACTION;
13976     }
13977
13978     if (IS_CUSTOM_ELEMENT(element) &&
13979         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
13980     {
13981       if (!DigFieldByCE(nextx, nexty, element))
13982         return MP_NO_ACTION;
13983     }
13984
13985     if (IS_SB_ELEMENT(element))
13986     {
13987       boolean sokoban_task_solved = FALSE;
13988
13989       if (element == EL_SOKOBAN_FIELD_FULL)
13990       {
13991         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13992
13993         IncrementSokobanFieldsNeeded();
13994         IncrementSokobanObjectsNeeded();
13995       }
13996
13997       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
13998       {
13999         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14000
14001         DecrementSokobanFieldsNeeded();
14002         DecrementSokobanObjectsNeeded();
14003
14004         // sokoban object was pushed from empty field to sokoban field
14005         if (Back[x][y] == EL_EMPTY)
14006           sokoban_task_solved = TRUE;
14007       }
14008
14009       Feld[x][y] = EL_SOKOBAN_OBJECT;
14010
14011       if (Back[x][y] == Back[nextx][nexty])
14012         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14013       else if (Back[x][y] != 0)
14014         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14015                                     ACTION_EMPTYING);
14016       else
14017         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14018                                     ACTION_FILLING);
14019
14020       if (sokoban_task_solved &&
14021           game.sokoban_fields_still_needed == 0 &&
14022           game.sokoban_objects_still_needed == 0 &&
14023           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
14024       {
14025         game.players_still_needed = 0;
14026
14027         LevelSolved();
14028
14029         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14030       }
14031     }
14032     else
14033       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14034
14035     InitMovingField(x, y, move_direction);
14036     GfxAction[x][y] = ACTION_PUSHING;
14037
14038     if (mode == DF_SNAP)
14039       ContinueMoving(x, y);
14040     else
14041       MovPos[x][y] = (dx != 0 ? dx : dy);
14042
14043     Pushed[x][y] = TRUE;
14044     Pushed[nextx][nexty] = TRUE;
14045
14046     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14047       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14048     else
14049       player->push_delay_value = -1;    // get new value later
14050
14051     // check for element change _after_ element has been pushed
14052     if (game.use_change_when_pushing_bug)
14053     {
14054       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14055                                  player->index_bit, dig_side);
14056       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14057                                           player->index_bit, dig_side);
14058     }
14059   }
14060   else if (IS_SWITCHABLE(element))
14061   {
14062     if (PLAYER_SWITCHING(player, x, y))
14063     {
14064       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14065                                           player->index_bit, dig_side);
14066
14067       return MP_ACTION;
14068     }
14069
14070     player->is_switching = TRUE;
14071     player->switch_x = x;
14072     player->switch_y = y;
14073
14074     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14075
14076     if (element == EL_ROBOT_WHEEL)
14077     {
14078       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14079
14080       game.robot_wheel_x = x;
14081       game.robot_wheel_y = y;
14082       game.robot_wheel_active = TRUE;
14083
14084       TEST_DrawLevelField(x, y);
14085     }
14086     else if (element == EL_SP_TERMINAL)
14087     {
14088       int xx, yy;
14089
14090       SCAN_PLAYFIELD(xx, yy)
14091       {
14092         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
14093         {
14094           Bang(xx, yy);
14095         }
14096         else if (Feld[xx][yy] == EL_SP_TERMINAL)
14097         {
14098           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14099
14100           ResetGfxAnimation(xx, yy);
14101           TEST_DrawLevelField(xx, yy);
14102         }
14103       }
14104     }
14105     else if (IS_BELT_SWITCH(element))
14106     {
14107       ToggleBeltSwitch(x, y);
14108     }
14109     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14110              element == EL_SWITCHGATE_SWITCH_DOWN ||
14111              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14112              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14113     {
14114       ToggleSwitchgateSwitch(x, y);
14115     }
14116     else if (element == EL_LIGHT_SWITCH ||
14117              element == EL_LIGHT_SWITCH_ACTIVE)
14118     {
14119       ToggleLightSwitch(x, y);
14120     }
14121     else if (element == EL_TIMEGATE_SWITCH ||
14122              element == EL_DC_TIMEGATE_SWITCH)
14123     {
14124       ActivateTimegateSwitch(x, y);
14125     }
14126     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14127              element == EL_BALLOON_SWITCH_RIGHT ||
14128              element == EL_BALLOON_SWITCH_UP    ||
14129              element == EL_BALLOON_SWITCH_DOWN  ||
14130              element == EL_BALLOON_SWITCH_NONE  ||
14131              element == EL_BALLOON_SWITCH_ANY)
14132     {
14133       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14134                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14135                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14136                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14137                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14138                              move_direction);
14139     }
14140     else if (element == EL_LAMP)
14141     {
14142       Feld[x][y] = EL_LAMP_ACTIVE;
14143       game.lights_still_needed--;
14144
14145       ResetGfxAnimation(x, y);
14146       TEST_DrawLevelField(x, y);
14147     }
14148     else if (element == EL_TIME_ORB_FULL)
14149     {
14150       Feld[x][y] = EL_TIME_ORB_EMPTY;
14151
14152       if (level.time > 0 || level.use_time_orb_bug)
14153       {
14154         TimeLeft += level.time_orb_time;
14155         game.no_time_limit = FALSE;
14156
14157         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14158
14159         DisplayGameControlValues();
14160       }
14161
14162       ResetGfxAnimation(x, y);
14163       TEST_DrawLevelField(x, y);
14164     }
14165     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14166              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14167     {
14168       int xx, yy;
14169
14170       game.ball_state = !game.ball_state;
14171
14172       SCAN_PLAYFIELD(xx, yy)
14173       {
14174         int e = Feld[xx][yy];
14175
14176         if (game.ball_state)
14177         {
14178           if (e == EL_EMC_MAGIC_BALL)
14179             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14180           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14181             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14182         }
14183         else
14184         {
14185           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14186             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14187           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14188             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14189         }
14190       }
14191     }
14192
14193     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14194                                         player->index_bit, dig_side);
14195
14196     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14197                                         player->index_bit, dig_side);
14198
14199     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14200                                         player->index_bit, dig_side);
14201
14202     return MP_ACTION;
14203   }
14204   else
14205   {
14206     if (!PLAYER_SWITCHING(player, x, y))
14207     {
14208       player->is_switching = TRUE;
14209       player->switch_x = x;
14210       player->switch_y = y;
14211
14212       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14213                                  player->index_bit, dig_side);
14214       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14215                                           player->index_bit, dig_side);
14216
14217       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14218                                  player->index_bit, dig_side);
14219       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14220                                           player->index_bit, dig_side);
14221     }
14222
14223     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14224                                player->index_bit, dig_side);
14225     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14226                                         player->index_bit, dig_side);
14227
14228     return MP_NO_ACTION;
14229   }
14230
14231   player->push_delay = -1;
14232
14233   if (is_player)                // function can also be called by EL_PENGUIN
14234   {
14235     if (Feld[x][y] != element)          // really digged/collected something
14236     {
14237       player->is_collecting = !player->is_digging;
14238       player->is_active = TRUE;
14239     }
14240   }
14241
14242   return MP_MOVING;
14243 }
14244
14245 static boolean DigFieldByCE(int x, int y, int digging_element)
14246 {
14247   int element = Feld[x][y];
14248
14249   if (!IS_FREE(x, y))
14250   {
14251     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14252                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14253                   ACTION_BREAKING);
14254
14255     // no element can dig solid indestructible elements
14256     if (IS_INDESTRUCTIBLE(element) &&
14257         !IS_DIGGABLE(element) &&
14258         !IS_COLLECTIBLE(element))
14259       return FALSE;
14260
14261     if (AmoebaNr[x][y] &&
14262         (element == EL_AMOEBA_FULL ||
14263          element == EL_BD_AMOEBA ||
14264          element == EL_AMOEBA_GROWING))
14265     {
14266       AmoebaCnt[AmoebaNr[x][y]]--;
14267       AmoebaCnt2[AmoebaNr[x][y]]--;
14268     }
14269
14270     if (IS_MOVING(x, y))
14271       RemoveMovingField(x, y);
14272     else
14273     {
14274       RemoveField(x, y);
14275       TEST_DrawLevelField(x, y);
14276     }
14277
14278     // if digged element was about to explode, prevent the explosion
14279     ExplodeField[x][y] = EX_TYPE_NONE;
14280
14281     PlayLevelSoundAction(x, y, action);
14282   }
14283
14284   Store[x][y] = EL_EMPTY;
14285
14286   // this makes it possible to leave the removed element again
14287   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14288     Store[x][y] = element;
14289
14290   return TRUE;
14291 }
14292
14293 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14294 {
14295   int jx = player->jx, jy = player->jy;
14296   int x = jx + dx, y = jy + dy;
14297   int snap_direction = (dx == -1 ? MV_LEFT  :
14298                         dx == +1 ? MV_RIGHT :
14299                         dy == -1 ? MV_UP    :
14300                         dy == +1 ? MV_DOWN  : MV_NONE);
14301   boolean can_continue_snapping = (level.continuous_snapping &&
14302                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14303
14304   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14305     return FALSE;
14306
14307   if (!player->active || !IN_LEV_FIELD(x, y))
14308     return FALSE;
14309
14310   if (dx && dy)
14311     return FALSE;
14312
14313   if (!dx && !dy)
14314   {
14315     if (player->MovPos == 0)
14316       player->is_pushing = FALSE;
14317
14318     player->is_snapping = FALSE;
14319
14320     if (player->MovPos == 0)
14321     {
14322       player->is_moving = FALSE;
14323       player->is_digging = FALSE;
14324       player->is_collecting = FALSE;
14325     }
14326
14327     return FALSE;
14328   }
14329
14330   // prevent snapping with already pressed snap key when not allowed
14331   if (player->is_snapping && !can_continue_snapping)
14332     return FALSE;
14333
14334   player->MovDir = snap_direction;
14335
14336   if (player->MovPos == 0)
14337   {
14338     player->is_moving = FALSE;
14339     player->is_digging = FALSE;
14340     player->is_collecting = FALSE;
14341   }
14342
14343   player->is_dropping = FALSE;
14344   player->is_dropping_pressed = FALSE;
14345   player->drop_pressed_delay = 0;
14346
14347   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14348     return FALSE;
14349
14350   player->is_snapping = TRUE;
14351   player->is_active = TRUE;
14352
14353   if (player->MovPos == 0)
14354   {
14355     player->is_moving = FALSE;
14356     player->is_digging = FALSE;
14357     player->is_collecting = FALSE;
14358   }
14359
14360   if (player->MovPos != 0)      // prevent graphic bugs in versions < 2.2.0
14361     TEST_DrawLevelField(player->last_jx, player->last_jy);
14362
14363   TEST_DrawLevelField(x, y);
14364
14365   return TRUE;
14366 }
14367
14368 static boolean DropElement(struct PlayerInfo *player)
14369 {
14370   int old_element, new_element;
14371   int dropx = player->jx, dropy = player->jy;
14372   int drop_direction = player->MovDir;
14373   int drop_side = drop_direction;
14374   int drop_element = get_next_dropped_element(player);
14375
14376   /* do not drop an element on top of another element; when holding drop key
14377      pressed without moving, dropped element must move away before the next
14378      element can be dropped (this is especially important if the next element
14379      is dynamite, which can be placed on background for historical reasons) */
14380   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14381     return MP_ACTION;
14382
14383   if (IS_THROWABLE(drop_element))
14384   {
14385     dropx += GET_DX_FROM_DIR(drop_direction);
14386     dropy += GET_DY_FROM_DIR(drop_direction);
14387
14388     if (!IN_LEV_FIELD(dropx, dropy))
14389       return FALSE;
14390   }
14391
14392   old_element = Feld[dropx][dropy];     // old element at dropping position
14393   new_element = drop_element;           // default: no change when dropping
14394
14395   // check if player is active, not moving and ready to drop
14396   if (!player->active || player->MovPos || player->drop_delay > 0)
14397     return FALSE;
14398
14399   // check if player has anything that can be dropped
14400   if (new_element == EL_UNDEFINED)
14401     return FALSE;
14402
14403   // only set if player has anything that can be dropped
14404   player->is_dropping_pressed = TRUE;
14405
14406   // check if drop key was pressed long enough for EM style dynamite
14407   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14408     return FALSE;
14409
14410   // check if anything can be dropped at the current position
14411   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14412     return FALSE;
14413
14414   // collected custom elements can only be dropped on empty fields
14415   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14416     return FALSE;
14417
14418   if (old_element != EL_EMPTY)
14419     Back[dropx][dropy] = old_element;   // store old element on this field
14420
14421   ResetGfxAnimation(dropx, dropy);
14422   ResetRandomAnimationValue(dropx, dropy);
14423
14424   if (player->inventory_size > 0 ||
14425       player->inventory_infinite_element != EL_UNDEFINED)
14426   {
14427     if (player->inventory_size > 0)
14428     {
14429       player->inventory_size--;
14430
14431       DrawGameDoorValues();
14432
14433       if (new_element == EL_DYNAMITE)
14434         new_element = EL_DYNAMITE_ACTIVE;
14435       else if (new_element == EL_EM_DYNAMITE)
14436         new_element = EL_EM_DYNAMITE_ACTIVE;
14437       else if (new_element == EL_SP_DISK_RED)
14438         new_element = EL_SP_DISK_RED_ACTIVE;
14439     }
14440
14441     Feld[dropx][dropy] = new_element;
14442
14443     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14444       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14445                           el2img(Feld[dropx][dropy]), 0);
14446
14447     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14448
14449     // needed if previous element just changed to "empty" in the last frame
14450     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14451
14452     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14453                                player->index_bit, drop_side);
14454     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14455                                         CE_PLAYER_DROPS_X,
14456                                         player->index_bit, drop_side);
14457
14458     TestIfElementTouchesCustomElement(dropx, dropy);
14459   }
14460   else          // player is dropping a dyna bomb
14461   {
14462     player->dynabombs_left--;
14463
14464     Feld[dropx][dropy] = new_element;
14465
14466     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14467       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14468                           el2img(Feld[dropx][dropy]), 0);
14469
14470     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14471   }
14472
14473   if (Feld[dropx][dropy] == new_element) // uninitialized unless CE change
14474     InitField_WithBug1(dropx, dropy, FALSE);
14475
14476   new_element = Feld[dropx][dropy];     // element might have changed
14477
14478   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14479       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14480   {
14481     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14482       MovDir[dropx][dropy] = drop_direction;
14483
14484     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14485
14486     // do not cause impact style collision by dropping elements that can fall
14487     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14488   }
14489
14490   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14491   player->is_dropping = TRUE;
14492
14493   player->drop_pressed_delay = 0;
14494   player->is_dropping_pressed = FALSE;
14495
14496   player->drop_x = dropx;
14497   player->drop_y = dropy;
14498
14499   return TRUE;
14500 }
14501
14502 // ----------------------------------------------------------------------------
14503 // game sound playing functions
14504 // ----------------------------------------------------------------------------
14505
14506 static int *loop_sound_frame = NULL;
14507 static int *loop_sound_volume = NULL;
14508
14509 void InitPlayLevelSound(void)
14510 {
14511   int num_sounds = getSoundListSize();
14512
14513   checked_free(loop_sound_frame);
14514   checked_free(loop_sound_volume);
14515
14516   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14517   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14518 }
14519
14520 static void PlayLevelSound(int x, int y, int nr)
14521 {
14522   int sx = SCREENX(x), sy = SCREENY(y);
14523   int volume, stereo_position;
14524   int max_distance = 8;
14525   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14526
14527   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14528       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14529     return;
14530
14531   if (!IN_LEV_FIELD(x, y) ||
14532       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14533       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14534     return;
14535
14536   volume = SOUND_MAX_VOLUME;
14537
14538   if (!IN_SCR_FIELD(sx, sy))
14539   {
14540     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14541     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14542
14543     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14544   }
14545
14546   stereo_position = (SOUND_MAX_LEFT +
14547                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14548                      (SCR_FIELDX + 2 * max_distance));
14549
14550   if (IS_LOOP_SOUND(nr))
14551   {
14552     /* This assures that quieter loop sounds do not overwrite louder ones,
14553        while restarting sound volume comparison with each new game frame. */
14554
14555     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14556       return;
14557
14558     loop_sound_volume[nr] = volume;
14559     loop_sound_frame[nr] = FrameCounter;
14560   }
14561
14562   PlaySoundExt(nr, volume, stereo_position, type);
14563 }
14564
14565 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14566 {
14567   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14568                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14569                  y < LEVELY(BY1) ? LEVELY(BY1) :
14570                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14571                  sound_action);
14572 }
14573
14574 static void PlayLevelSoundAction(int x, int y, int action)
14575 {
14576   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14577 }
14578
14579 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14580 {
14581   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14582
14583   if (sound_effect != SND_UNDEFINED)
14584     PlayLevelSound(x, y, sound_effect);
14585 }
14586
14587 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14588                                               int action)
14589 {
14590   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14591
14592   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14593     PlayLevelSound(x, y, sound_effect);
14594 }
14595
14596 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14597 {
14598   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14599
14600   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14601     PlayLevelSound(x, y, sound_effect);
14602 }
14603
14604 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14605 {
14606   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14607
14608   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14609     StopSound(sound_effect);
14610 }
14611
14612 static int getLevelMusicNr(void)
14613 {
14614   if (levelset.music[level_nr] != MUS_UNDEFINED)
14615     return levelset.music[level_nr];            // from config file
14616   else
14617     return MAP_NOCONF_MUSIC(level_nr);          // from music dir
14618 }
14619
14620 static void FadeLevelSounds(void)
14621 {
14622   FadeSounds();
14623 }
14624
14625 static void FadeLevelMusic(void)
14626 {
14627   int music_nr = getLevelMusicNr();
14628   char *curr_music = getCurrentlyPlayingMusicFilename();
14629   char *next_music = getMusicInfoEntryFilename(music_nr);
14630
14631   if (!strEqual(curr_music, next_music))
14632     FadeMusic();
14633 }
14634
14635 void FadeLevelSoundsAndMusic(void)
14636 {
14637   FadeLevelSounds();
14638   FadeLevelMusic();
14639 }
14640
14641 static void PlayLevelMusic(void)
14642 {
14643   int music_nr = getLevelMusicNr();
14644   char *curr_music = getCurrentlyPlayingMusicFilename();
14645   char *next_music = getMusicInfoEntryFilename(music_nr);
14646
14647   if (!strEqual(curr_music, next_music))
14648     PlayMusicLoop(music_nr);
14649 }
14650
14651 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14652 {
14653   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14654   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14655   int x = xx - 1 - offset;
14656   int y = yy - 1 - offset;
14657
14658   switch (sample)
14659   {
14660     case SAMPLE_blank:
14661       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14662       break;
14663
14664     case SAMPLE_roll:
14665       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14666       break;
14667
14668     case SAMPLE_stone:
14669       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14670       break;
14671
14672     case SAMPLE_nut:
14673       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14674       break;
14675
14676     case SAMPLE_crack:
14677       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14678       break;
14679
14680     case SAMPLE_bug:
14681       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14682       break;
14683
14684     case SAMPLE_tank:
14685       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14686       break;
14687
14688     case SAMPLE_android_clone:
14689       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14690       break;
14691
14692     case SAMPLE_android_move:
14693       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14694       break;
14695
14696     case SAMPLE_spring:
14697       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14698       break;
14699
14700     case SAMPLE_slurp:
14701       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14702       break;
14703
14704     case SAMPLE_eater:
14705       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14706       break;
14707
14708     case SAMPLE_eater_eat:
14709       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14710       break;
14711
14712     case SAMPLE_alien:
14713       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14714       break;
14715
14716     case SAMPLE_collect:
14717       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14718       break;
14719
14720     case SAMPLE_diamond:
14721       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14722       break;
14723
14724     case SAMPLE_squash:
14725       // !!! CHECK THIS !!!
14726 #if 1
14727       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14728 #else
14729       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14730 #endif
14731       break;
14732
14733     case SAMPLE_wonderfall:
14734       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14735       break;
14736
14737     case SAMPLE_drip:
14738       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14739       break;
14740
14741     case SAMPLE_push:
14742       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14743       break;
14744
14745     case SAMPLE_dirt:
14746       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14747       break;
14748
14749     case SAMPLE_acid:
14750       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14751       break;
14752
14753     case SAMPLE_ball:
14754       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14755       break;
14756
14757     case SAMPLE_grow:
14758       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14759       break;
14760
14761     case SAMPLE_wonder:
14762       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14763       break;
14764
14765     case SAMPLE_door:
14766       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14767       break;
14768
14769     case SAMPLE_exit_open:
14770       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14771       break;
14772
14773     case SAMPLE_exit_leave:
14774       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14775       break;
14776
14777     case SAMPLE_dynamite:
14778       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14779       break;
14780
14781     case SAMPLE_tick:
14782       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14783       break;
14784
14785     case SAMPLE_press:
14786       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14787       break;
14788
14789     case SAMPLE_wheel:
14790       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14791       break;
14792
14793     case SAMPLE_boom:
14794       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14795       break;
14796
14797     case SAMPLE_die:
14798       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14799       break;
14800
14801     case SAMPLE_time:
14802       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14803       break;
14804
14805     default:
14806       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14807       break;
14808   }
14809 }
14810
14811 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
14812 {
14813   int element = map_element_SP_to_RND(element_sp);
14814   int action = map_action_SP_to_RND(action_sp);
14815   int offset = (setup.sp_show_border_elements ? 0 : 1);
14816   int x = xx - offset;
14817   int y = yy - offset;
14818
14819   PlayLevelSoundElementAction(x, y, element, action);
14820 }
14821
14822 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
14823 {
14824   int element = map_element_MM_to_RND(element_mm);
14825   int action = map_action_MM_to_RND(action_mm);
14826   int offset = 0;
14827   int x = xx - offset;
14828   int y = yy - offset;
14829
14830   if (!IS_MM_ELEMENT(element))
14831     element = EL_MM_DEFAULT;
14832
14833   PlayLevelSoundElementAction(x, y, element, action);
14834 }
14835
14836 void PlaySound_MM(int sound_mm)
14837 {
14838   int sound = map_sound_MM_to_RND(sound_mm);
14839
14840   if (sound == SND_UNDEFINED)
14841     return;
14842
14843   PlaySound(sound);
14844 }
14845
14846 void PlaySoundLoop_MM(int sound_mm)
14847 {
14848   int sound = map_sound_MM_to_RND(sound_mm);
14849
14850   if (sound == SND_UNDEFINED)
14851     return;
14852
14853   PlaySoundLoop(sound);
14854 }
14855
14856 void StopSound_MM(int sound_mm)
14857 {
14858   int sound = map_sound_MM_to_RND(sound_mm);
14859
14860   if (sound == SND_UNDEFINED)
14861     return;
14862
14863   StopSound(sound);
14864 }
14865
14866 void RaiseScore(int value)
14867 {
14868   game.score += value;
14869
14870   game_panel_controls[GAME_PANEL_SCORE].value = game.score;
14871
14872   DisplayGameControlValues();
14873 }
14874
14875 void RaiseScoreElement(int element)
14876 {
14877   switch (element)
14878   {
14879     case EL_EMERALD:
14880     case EL_BD_DIAMOND:
14881     case EL_EMERALD_YELLOW:
14882     case EL_EMERALD_RED:
14883     case EL_EMERALD_PURPLE:
14884     case EL_SP_INFOTRON:
14885       RaiseScore(level.score[SC_EMERALD]);
14886       break;
14887     case EL_DIAMOND:
14888       RaiseScore(level.score[SC_DIAMOND]);
14889       break;
14890     case EL_CRYSTAL:
14891       RaiseScore(level.score[SC_CRYSTAL]);
14892       break;
14893     case EL_PEARL:
14894       RaiseScore(level.score[SC_PEARL]);
14895       break;
14896     case EL_BUG:
14897     case EL_BD_BUTTERFLY:
14898     case EL_SP_ELECTRON:
14899       RaiseScore(level.score[SC_BUG]);
14900       break;
14901     case EL_SPACESHIP:
14902     case EL_BD_FIREFLY:
14903     case EL_SP_SNIKSNAK:
14904       RaiseScore(level.score[SC_SPACESHIP]);
14905       break;
14906     case EL_YAMYAM:
14907     case EL_DARK_YAMYAM:
14908       RaiseScore(level.score[SC_YAMYAM]);
14909       break;
14910     case EL_ROBOT:
14911       RaiseScore(level.score[SC_ROBOT]);
14912       break;
14913     case EL_PACMAN:
14914       RaiseScore(level.score[SC_PACMAN]);
14915       break;
14916     case EL_NUT:
14917       RaiseScore(level.score[SC_NUT]);
14918       break;
14919     case EL_DYNAMITE:
14920     case EL_EM_DYNAMITE:
14921     case EL_SP_DISK_RED:
14922     case EL_DYNABOMB_INCREASE_NUMBER:
14923     case EL_DYNABOMB_INCREASE_SIZE:
14924     case EL_DYNABOMB_INCREASE_POWER:
14925       RaiseScore(level.score[SC_DYNAMITE]);
14926       break;
14927     case EL_SHIELD_NORMAL:
14928     case EL_SHIELD_DEADLY:
14929       RaiseScore(level.score[SC_SHIELD]);
14930       break;
14931     case EL_EXTRA_TIME:
14932       RaiseScore(level.extra_time_score);
14933       break;
14934     case EL_KEY_1:
14935     case EL_KEY_2:
14936     case EL_KEY_3:
14937     case EL_KEY_4:
14938     case EL_EM_KEY_1:
14939     case EL_EM_KEY_2:
14940     case EL_EM_KEY_3:
14941     case EL_EM_KEY_4:
14942     case EL_EMC_KEY_5:
14943     case EL_EMC_KEY_6:
14944     case EL_EMC_KEY_7:
14945     case EL_EMC_KEY_8:
14946     case EL_DC_KEY_WHITE:
14947       RaiseScore(level.score[SC_KEY]);
14948       break;
14949     default:
14950       RaiseScore(element_info[element].collect_score);
14951       break;
14952   }
14953 }
14954
14955 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14956 {
14957   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14958   {
14959     // closing door required in case of envelope style request dialogs
14960     if (!skip_request)
14961       CloseDoor(DOOR_CLOSE_1);
14962
14963     if (network.enabled)
14964       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14965     else
14966     {
14967       if (quick_quit)
14968         FadeSkipNextFadeIn();
14969
14970       SetGameStatus(GAME_MODE_MAIN);
14971
14972       DrawMainMenu();
14973     }
14974   }
14975   else          // continue playing the game
14976   {
14977     if (tape.playing && tape.deactivate_display)
14978       TapeDeactivateDisplayOff(TRUE);
14979
14980     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14981
14982     if (tape.playing && tape.deactivate_display)
14983       TapeDeactivateDisplayOn();
14984   }
14985 }
14986
14987 void RequestQuitGame(boolean ask_if_really_quit)
14988 {
14989   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14990   boolean skip_request = game.all_players_gone || quick_quit;
14991
14992   RequestQuitGameExt(skip_request, quick_quit,
14993                      "Do you really want to quit the game?");
14994 }
14995
14996 void RequestRestartGame(char *message)
14997 {
14998   game.restart_game_message = NULL;
14999
15000   boolean has_started_game = hasStartedNetworkGame();
15001   int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM);
15002
15003   if (Request(message, request_mode | REQ_STAY_CLOSED) && has_started_game)
15004   {
15005     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15006   }
15007   else
15008   {
15009     SetGameStatus(GAME_MODE_MAIN);
15010
15011     DrawMainMenu();
15012   }
15013 }
15014
15015 void CheckGameOver(void)
15016 {
15017   static boolean last_game_over = FALSE;
15018   static int game_over_delay = 0;
15019   int game_over_delay_value = 50;
15020   boolean game_over = checkGameFailed();
15021
15022   // do not handle game over if request dialog is already active
15023   if (game.request_active)
15024     return;
15025
15026   if (!game_over)
15027   {
15028     last_game_over = FALSE;
15029     game_over_delay = game_over_delay_value;
15030
15031     return;
15032   }
15033
15034   if (game_over_delay > 0)
15035   {
15036     game_over_delay--;
15037
15038     return;
15039   }
15040
15041   if (last_game_over != game_over)
15042     game.restart_game_message = (hasStartedNetworkGame() ?
15043                                  "Game over! Play it again?" :
15044                                  "Game over!");
15045
15046   last_game_over = game_over;
15047 }
15048
15049 boolean checkGameSolved(void)
15050 {
15051   // set for all game engines if level was solved
15052   return game.LevelSolved_GameEnd;
15053 }
15054
15055 boolean checkGameFailed(void)
15056 {
15057   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15058     return (game_em.game_over && !game_em.level_solved);
15059   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15060     return (game_sp.game_over && !game_sp.level_solved);
15061   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15062     return (game_mm.game_over && !game_mm.level_solved);
15063   else                          // GAME_ENGINE_TYPE_RND
15064     return (game.GameOver && !game.LevelSolved);
15065 }
15066
15067 boolean checkGameEnded(void)
15068 {
15069   return (checkGameSolved() || checkGameFailed());
15070 }
15071
15072
15073 // ----------------------------------------------------------------------------
15074 // random generator functions
15075 // ----------------------------------------------------------------------------
15076
15077 unsigned int InitEngineRandom_RND(int seed)
15078 {
15079   game.num_random_calls = 0;
15080
15081   return InitEngineRandom(seed);
15082 }
15083
15084 unsigned int RND(int max)
15085 {
15086   if (max > 0)
15087   {
15088     game.num_random_calls++;
15089
15090     return GetEngineRandom(max);
15091   }
15092
15093   return 0;
15094 }
15095
15096
15097 // ----------------------------------------------------------------------------
15098 // game engine snapshot handling functions
15099 // ----------------------------------------------------------------------------
15100
15101 struct EngineSnapshotInfo
15102 {
15103   // runtime values for custom element collect score
15104   int collect_score[NUM_CUSTOM_ELEMENTS];
15105
15106   // runtime values for group element choice position
15107   int choice_pos[NUM_GROUP_ELEMENTS];
15108
15109   // runtime values for belt position animations
15110   int belt_graphic[4][NUM_BELT_PARTS];
15111   int belt_anim_mode[4][NUM_BELT_PARTS];
15112 };
15113
15114 static struct EngineSnapshotInfo engine_snapshot_rnd;
15115 static char *snapshot_level_identifier = NULL;
15116 static int snapshot_level_nr = -1;
15117
15118 static void SaveEngineSnapshotValues_RND(void)
15119 {
15120   static int belt_base_active_element[4] =
15121   {
15122     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15123     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15124     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15125     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15126   };
15127   int i, j;
15128
15129   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15130   {
15131     int element = EL_CUSTOM_START + i;
15132
15133     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15134   }
15135
15136   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15137   {
15138     int element = EL_GROUP_START + i;
15139
15140     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15141   }
15142
15143   for (i = 0; i < 4; i++)
15144   {
15145     for (j = 0; j < NUM_BELT_PARTS; j++)
15146     {
15147       int element = belt_base_active_element[i] + j;
15148       int graphic = el2img(element);
15149       int anim_mode = graphic_info[graphic].anim_mode;
15150
15151       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15152       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15153     }
15154   }
15155 }
15156
15157 static void LoadEngineSnapshotValues_RND(void)
15158 {
15159   unsigned int num_random_calls = game.num_random_calls;
15160   int i, j;
15161
15162   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15163   {
15164     int element = EL_CUSTOM_START + i;
15165
15166     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15167   }
15168
15169   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15170   {
15171     int element = EL_GROUP_START + i;
15172
15173     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15174   }
15175
15176   for (i = 0; i < 4; i++)
15177   {
15178     for (j = 0; j < NUM_BELT_PARTS; j++)
15179     {
15180       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15181       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15182
15183       graphic_info[graphic].anim_mode = anim_mode;
15184     }
15185   }
15186
15187   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15188   {
15189     InitRND(tape.random_seed);
15190     for (i = 0; i < num_random_calls; i++)
15191       RND(1);
15192   }
15193
15194   if (game.num_random_calls != num_random_calls)
15195   {
15196     Error(ERR_INFO, "number of random calls out of sync");
15197     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
15198     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
15199     Error(ERR_EXIT, "this should not happen -- please debug");
15200   }
15201 }
15202
15203 void FreeEngineSnapshotSingle(void)
15204 {
15205   FreeSnapshotSingle();
15206
15207   setString(&snapshot_level_identifier, NULL);
15208   snapshot_level_nr = -1;
15209 }
15210
15211 void FreeEngineSnapshotList(void)
15212 {
15213   FreeSnapshotList();
15214 }
15215
15216 static ListNode *SaveEngineSnapshotBuffers(void)
15217 {
15218   ListNode *buffers = NULL;
15219
15220   // copy some special values to a structure better suited for the snapshot
15221
15222   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15223     SaveEngineSnapshotValues_RND();
15224   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15225     SaveEngineSnapshotValues_EM();
15226   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15227     SaveEngineSnapshotValues_SP(&buffers);
15228   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15229     SaveEngineSnapshotValues_MM(&buffers);
15230
15231   // save values stored in special snapshot structure
15232
15233   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15234     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15235   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15236     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15237   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15238     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15239   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15240     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15241
15242   // save further RND engine values
15243
15244   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15245   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15246   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15247
15248   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15249   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15250   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15251   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15252   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15253
15254   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15255   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15256   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15257
15258   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15259
15260   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15261   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15262
15263   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Feld));
15264   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15265   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15266   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15267   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15268   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15269   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15270   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15271   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15272   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15273   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15274   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15275   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15276   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15277   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15278   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15279   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15280   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15281
15282   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15283   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15284
15285   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15286   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15287   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15288
15289   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15290   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15291
15292   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15293   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15294   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15295   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15296   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15297
15298   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15299   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15300
15301 #if 0
15302   ListNode *node = engine_snapshot_list_rnd;
15303   int num_bytes = 0;
15304
15305   while (node != NULL)
15306   {
15307     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15308
15309     node = node->next;
15310   }
15311
15312   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
15313 #endif
15314
15315   return buffers;
15316 }
15317
15318 void SaveEngineSnapshotSingle(void)
15319 {
15320   ListNode *buffers = SaveEngineSnapshotBuffers();
15321
15322   // finally save all snapshot buffers to single snapshot
15323   SaveSnapshotSingle(buffers);
15324
15325   // save level identification information
15326   setString(&snapshot_level_identifier, leveldir_current->identifier);
15327   snapshot_level_nr = level_nr;
15328 }
15329
15330 boolean CheckSaveEngineSnapshotToList(void)
15331 {
15332   boolean save_snapshot =
15333     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
15334      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
15335       game.snapshot.changed_action) ||
15336      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15337       game.snapshot.collected_item));
15338
15339   game.snapshot.changed_action = FALSE;
15340   game.snapshot.collected_item = FALSE;
15341   game.snapshot.save_snapshot = save_snapshot;
15342
15343   return save_snapshot;
15344 }
15345
15346 void SaveEngineSnapshotToList(void)
15347 {
15348   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
15349       tape.quick_resume)
15350     return;
15351
15352   ListNode *buffers = SaveEngineSnapshotBuffers();
15353
15354   // finally save all snapshot buffers to snapshot list
15355   SaveSnapshotToList(buffers);
15356 }
15357
15358 void SaveEngineSnapshotToListInitial(void)
15359 {
15360   FreeEngineSnapshotList();
15361
15362   SaveEngineSnapshotToList();
15363 }
15364
15365 static void LoadEngineSnapshotValues(void)
15366 {
15367   // restore special values from snapshot structure
15368
15369   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15370     LoadEngineSnapshotValues_RND();
15371   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15372     LoadEngineSnapshotValues_EM();
15373   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15374     LoadEngineSnapshotValues_SP();
15375   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15376     LoadEngineSnapshotValues_MM();
15377 }
15378
15379 void LoadEngineSnapshotSingle(void)
15380 {
15381   LoadSnapshotSingle();
15382
15383   LoadEngineSnapshotValues();
15384 }
15385
15386 static void LoadEngineSnapshot_Undo(int steps)
15387 {
15388   LoadSnapshotFromList_Older(steps);
15389
15390   LoadEngineSnapshotValues();
15391 }
15392
15393 static void LoadEngineSnapshot_Redo(int steps)
15394 {
15395   LoadSnapshotFromList_Newer(steps);
15396
15397   LoadEngineSnapshotValues();
15398 }
15399
15400 boolean CheckEngineSnapshotSingle(void)
15401 {
15402   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15403           snapshot_level_nr == level_nr);
15404 }
15405
15406 boolean CheckEngineSnapshotList(void)
15407 {
15408   return CheckSnapshotList();
15409 }
15410
15411
15412 // ---------- new game button stuff -------------------------------------------
15413
15414 static struct
15415 {
15416   int graphic;
15417   struct XY *pos;
15418   int gadget_id;
15419   boolean *setup_value;
15420   boolean allowed_on_tape;
15421   char *infotext;
15422 } gamebutton_info[NUM_GAME_BUTTONS] =
15423 {
15424   {
15425     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
15426     GAME_CTRL_ID_STOP,                          NULL,
15427     TRUE,                                       "stop game"
15428   },
15429   {
15430     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
15431     GAME_CTRL_ID_PAUSE,                         NULL,
15432     TRUE,                                       "pause game"
15433   },
15434   {
15435     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
15436     GAME_CTRL_ID_PLAY,                          NULL,
15437     TRUE,                                       "play game"
15438   },
15439   {
15440     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
15441     GAME_CTRL_ID_UNDO,                          NULL,
15442     TRUE,                                       "undo step"
15443   },
15444   {
15445     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
15446     GAME_CTRL_ID_REDO,                          NULL,
15447     TRUE,                                       "redo step"
15448   },
15449   {
15450     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
15451     GAME_CTRL_ID_SAVE,                          NULL,
15452     TRUE,                                       "save game"
15453   },
15454   {
15455     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
15456     GAME_CTRL_ID_PAUSE2,                        NULL,
15457     TRUE,                                       "pause game"
15458   },
15459   {
15460     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
15461     GAME_CTRL_ID_LOAD,                          NULL,
15462     TRUE,                                       "load game"
15463   },
15464   {
15465     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
15466     GAME_CTRL_ID_PANEL_STOP,                    NULL,
15467     FALSE,                                      "stop game"
15468   },
15469   {
15470     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
15471     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
15472     FALSE,                                      "pause game"
15473   },
15474   {
15475     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
15476     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
15477     FALSE,                                      "play game"
15478   },
15479   {
15480     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
15481     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
15482     TRUE,                                       "background music on/off"
15483   },
15484   {
15485     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
15486     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
15487     TRUE,                                       "sound loops on/off"
15488   },
15489   {
15490     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
15491     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
15492     TRUE,                                       "normal sounds on/off"
15493   },
15494   {
15495     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
15496     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
15497     FALSE,                                      "background music on/off"
15498   },
15499   {
15500     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
15501     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
15502     FALSE,                                      "sound loops on/off"
15503   },
15504   {
15505     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
15506     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
15507     FALSE,                                      "normal sounds on/off"
15508   }
15509 };
15510
15511 void CreateGameButtons(void)
15512 {
15513   int i;
15514
15515   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15516   {
15517     int graphic = gamebutton_info[i].graphic;
15518     struct GraphicInfo *gfx = &graphic_info[graphic];
15519     struct XY *pos = gamebutton_info[i].pos;
15520     struct GadgetInfo *gi;
15521     int button_type;
15522     boolean checked;
15523     unsigned int event_mask;
15524     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
15525     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
15526     int base_x = (on_tape ? VX : DX);
15527     int base_y = (on_tape ? VY : DY);
15528     int gd_x   = gfx->src_x;
15529     int gd_y   = gfx->src_y;
15530     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
15531     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
15532     int gd_xa  = gfx->src_x + gfx->active_xoffset;
15533     int gd_ya  = gfx->src_y + gfx->active_yoffset;
15534     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
15535     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
15536     int id = i;
15537
15538     if (gfx->bitmap == NULL)
15539     {
15540       game_gadget[id] = NULL;
15541
15542       continue;
15543     }
15544
15545     if (id == GAME_CTRL_ID_STOP ||
15546         id == GAME_CTRL_ID_PANEL_STOP ||
15547         id == GAME_CTRL_ID_PLAY ||
15548         id == GAME_CTRL_ID_PANEL_PLAY ||
15549         id == GAME_CTRL_ID_SAVE ||
15550         id == GAME_CTRL_ID_LOAD)
15551     {
15552       button_type = GD_TYPE_NORMAL_BUTTON;
15553       checked = FALSE;
15554       event_mask = GD_EVENT_RELEASED;
15555     }
15556     else if (id == GAME_CTRL_ID_UNDO ||
15557              id == GAME_CTRL_ID_REDO)
15558     {
15559       button_type = GD_TYPE_NORMAL_BUTTON;
15560       checked = FALSE;
15561       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
15562     }
15563     else
15564     {
15565       button_type = GD_TYPE_CHECK_BUTTON;
15566       checked = (gamebutton_info[i].setup_value != NULL ?
15567                  *gamebutton_info[i].setup_value : FALSE);
15568       event_mask = GD_EVENT_PRESSED;
15569     }
15570
15571     gi = CreateGadget(GDI_CUSTOM_ID, id,
15572                       GDI_IMAGE_ID, graphic,
15573                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
15574                       GDI_X, base_x + GDI_ACTIVE_POS(pos->x),
15575                       GDI_Y, base_y + GDI_ACTIVE_POS(pos->y),
15576                       GDI_WIDTH, gfx->width,
15577                       GDI_HEIGHT, gfx->height,
15578                       GDI_TYPE, button_type,
15579                       GDI_STATE, GD_BUTTON_UNPRESSED,
15580                       GDI_CHECKED, checked,
15581                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
15582                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
15583                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
15584                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
15585                       GDI_DIRECT_DRAW, FALSE,
15586                       GDI_EVENT_MASK, event_mask,
15587                       GDI_CALLBACK_ACTION, HandleGameButtons,
15588                       GDI_END);
15589
15590     if (gi == NULL)
15591       Error(ERR_EXIT, "cannot create gadget");
15592
15593     game_gadget[id] = gi;
15594   }
15595 }
15596
15597 void FreeGameButtons(void)
15598 {
15599   int i;
15600
15601   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15602     FreeGadget(game_gadget[i]);
15603 }
15604
15605 static void UnmapGameButtonsAtSamePosition(int id)
15606 {
15607   int i;
15608
15609   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15610     if (i != id &&
15611         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15612         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15613       UnmapGadget(game_gadget[i]);
15614 }
15615
15616 static void UnmapGameButtonsAtSamePosition_All(void)
15617 {
15618   if (setup.show_snapshot_buttons)
15619   {
15620     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
15621     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
15622     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
15623   }
15624   else
15625   {
15626     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
15627     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
15628     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
15629
15630     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
15631     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
15632     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
15633   }
15634 }
15635
15636 static void MapGameButtonsAtSamePosition(int id)
15637 {
15638   int i;
15639
15640   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15641     if (i != id &&
15642         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15643         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15644       MapGadget(game_gadget[i]);
15645
15646   UnmapGameButtonsAtSamePosition_All();
15647 }
15648
15649 void MapUndoRedoButtons(void)
15650 {
15651   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15652   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15653
15654   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15655   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15656
15657   ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, TRUE, GDI_END);
15658 }
15659
15660 void UnmapUndoRedoButtons(void)
15661 {
15662   UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15663   UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15664
15665   MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15666   MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15667
15668   ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, FALSE, GDI_END);
15669 }
15670
15671 static void MapGameButtonsExt(boolean on_tape)
15672 {
15673   int i;
15674
15675   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15676     if ((!on_tape || gamebutton_info[i].allowed_on_tape) &&
15677         i != GAME_CTRL_ID_UNDO &&
15678         i != GAME_CTRL_ID_REDO)
15679       MapGadget(game_gadget[i]);
15680
15681   UnmapGameButtonsAtSamePosition_All();
15682
15683   RedrawGameButtons();
15684 }
15685
15686 static void UnmapGameButtonsExt(boolean on_tape)
15687 {
15688   int i;
15689
15690   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15691     if (!on_tape || gamebutton_info[i].allowed_on_tape)
15692       UnmapGadget(game_gadget[i]);
15693 }
15694
15695 static void RedrawGameButtonsExt(boolean on_tape)
15696 {
15697   int i;
15698
15699   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15700     if (!on_tape || gamebutton_info[i].allowed_on_tape)
15701       RedrawGadget(game_gadget[i]);
15702
15703   // RedrawGadget() may have set REDRAW_ALL if buttons are defined off-area
15704   redraw_mask &= ~REDRAW_ALL;
15705 }
15706
15707 static void SetGadgetState(struct GadgetInfo *gi, boolean state)
15708 {
15709   if (gi == NULL)
15710     return;
15711
15712   gi->checked = state;
15713 }
15714
15715 static void RedrawSoundButtonGadget(int id)
15716 {
15717   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
15718              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
15719              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
15720              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
15721              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
15722              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
15723              id);
15724
15725   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
15726   RedrawGadget(game_gadget[id2]);
15727 }
15728
15729 void MapGameButtons(void)
15730 {
15731   MapGameButtonsExt(FALSE);
15732 }
15733
15734 void UnmapGameButtons(void)
15735 {
15736   UnmapGameButtonsExt(FALSE);
15737 }
15738
15739 void RedrawGameButtons(void)
15740 {
15741   RedrawGameButtonsExt(FALSE);
15742 }
15743
15744 void MapGameButtonsOnTape(void)
15745 {
15746   MapGameButtonsExt(TRUE);
15747 }
15748
15749 void UnmapGameButtonsOnTape(void)
15750 {
15751   UnmapGameButtonsExt(TRUE);
15752 }
15753
15754 void RedrawGameButtonsOnTape(void)
15755 {
15756   RedrawGameButtonsExt(TRUE);
15757 }
15758
15759 static void GameUndoRedoExt(void)
15760 {
15761   ClearPlayerAction();
15762
15763   tape.pausing = TRUE;
15764
15765   RedrawPlayfield();
15766   UpdateAndDisplayGameControlValues();
15767
15768   DrawCompleteVideoDisplay();
15769   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
15770   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
15771   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
15772
15773   BackToFront();
15774 }
15775
15776 static void GameUndo(int steps)
15777 {
15778   if (!CheckEngineSnapshotList())
15779     return;
15780
15781   LoadEngineSnapshot_Undo(steps);
15782
15783   GameUndoRedoExt();
15784 }
15785
15786 static void GameRedo(int steps)
15787 {
15788   if (!CheckEngineSnapshotList())
15789     return;
15790
15791   LoadEngineSnapshot_Redo(steps);
15792
15793   GameUndoRedoExt();
15794 }
15795
15796 static void HandleGameButtonsExt(int id, int button)
15797 {
15798   static boolean game_undo_executed = FALSE;
15799   int steps = BUTTON_STEPSIZE(button);
15800   boolean handle_game_buttons =
15801     (game_status == GAME_MODE_PLAYING ||
15802      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
15803
15804   if (!handle_game_buttons)
15805     return;
15806
15807   switch (id)
15808   {
15809     case GAME_CTRL_ID_STOP:
15810     case GAME_CTRL_ID_PANEL_STOP:
15811       if (game_status == GAME_MODE_MAIN)
15812         break;
15813
15814       if (tape.playing)
15815         TapeStop();
15816       else
15817         RequestQuitGame(TRUE);
15818
15819       break;
15820
15821     case GAME_CTRL_ID_PAUSE:
15822     case GAME_CTRL_ID_PAUSE2:
15823     case GAME_CTRL_ID_PANEL_PAUSE:
15824       if (network.enabled && game_status == GAME_MODE_PLAYING)
15825       {
15826         if (tape.pausing)
15827           SendToServer_ContinuePlaying();
15828         else
15829           SendToServer_PausePlaying();
15830       }
15831       else
15832         TapeTogglePause(TAPE_TOGGLE_MANUAL);
15833
15834       game_undo_executed = FALSE;
15835
15836       break;
15837
15838     case GAME_CTRL_ID_PLAY:
15839     case GAME_CTRL_ID_PANEL_PLAY:
15840       if (game_status == GAME_MODE_MAIN)
15841       {
15842         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15843       }
15844       else if (tape.pausing)
15845       {
15846         if (network.enabled)
15847           SendToServer_ContinuePlaying();
15848         else
15849           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
15850       }
15851       break;
15852
15853     case GAME_CTRL_ID_UNDO:
15854       // Important: When using "save snapshot when collecting an item" mode,
15855       // load last (current) snapshot for first "undo" after pressing "pause"
15856       // (else the last-but-one snapshot would be loaded, because the snapshot
15857       // pointer already points to the last snapshot when pressing "pause",
15858       // which is fine for "every step/move" mode, but not for "every collect")
15859       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15860           !game_undo_executed)
15861         steps--;
15862
15863       game_undo_executed = TRUE;
15864
15865       GameUndo(steps);
15866       break;
15867
15868     case GAME_CTRL_ID_REDO:
15869       GameRedo(steps);
15870       break;
15871
15872     case GAME_CTRL_ID_SAVE:
15873       TapeQuickSave();
15874       break;
15875
15876     case GAME_CTRL_ID_LOAD:
15877       TapeQuickLoad();
15878       break;
15879
15880     case SOUND_CTRL_ID_MUSIC:
15881     case SOUND_CTRL_ID_PANEL_MUSIC:
15882       if (setup.sound_music)
15883       { 
15884         setup.sound_music = FALSE;
15885
15886         FadeMusic();
15887       }
15888       else if (audio.music_available)
15889       { 
15890         setup.sound = setup.sound_music = TRUE;
15891
15892         SetAudioMode(setup.sound);
15893
15894         if (game_status == GAME_MODE_PLAYING)
15895           PlayLevelMusic();
15896       }
15897
15898       RedrawSoundButtonGadget(id);
15899
15900       break;
15901
15902     case SOUND_CTRL_ID_LOOPS:
15903     case SOUND_CTRL_ID_PANEL_LOOPS:
15904       if (setup.sound_loops)
15905         setup.sound_loops = FALSE;
15906       else if (audio.loops_available)
15907       {
15908         setup.sound = setup.sound_loops = TRUE;
15909
15910         SetAudioMode(setup.sound);
15911       }
15912
15913       RedrawSoundButtonGadget(id);
15914
15915       break;
15916
15917     case SOUND_CTRL_ID_SIMPLE:
15918     case SOUND_CTRL_ID_PANEL_SIMPLE:
15919       if (setup.sound_simple)
15920         setup.sound_simple = FALSE;
15921       else if (audio.sound_available)
15922       {
15923         setup.sound = setup.sound_simple = TRUE;
15924
15925         SetAudioMode(setup.sound);
15926       }
15927
15928       RedrawSoundButtonGadget(id);
15929
15930       break;
15931
15932     default:
15933       break;
15934   }
15935 }
15936
15937 static void HandleGameButtons(struct GadgetInfo *gi)
15938 {
15939   HandleGameButtonsExt(gi->custom_id, gi->event.button);
15940 }
15941
15942 void HandleSoundButtonKeys(Key key)
15943 {
15944   if (key == setup.shortcut.sound_simple)
15945     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
15946   else if (key == setup.shortcut.sound_loops)
15947     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
15948   else if (key == setup.shortcut.sound_music)
15949     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
15950 }