23650858360e773404ce8b050b1bbffba1234266
[rocksndiamonds.git] / src / game.c
1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // game.c
10 // ============================================================================
11
12 #include "libgame/libgame.h"
13
14 #include "game.h"
15 #include "init.h"
16 #include "tools.h"
17 #include "screens.h"
18 #include "events.h"
19 #include "files.h"
20 #include "tape.h"
21 #include "network.h"
22 #include "anim.h"
23
24
25 // DEBUG SETTINGS
26 #define DEBUG_INIT_PLAYER       1
27 #define DEBUG_PLAYER_ACTIONS    0
28
29 // EXPERIMENTAL STUFF
30 #define USE_NEW_AMOEBA_CODE     FALSE
31
32 // EXPERIMENTAL STUFF
33 #define USE_QUICKSAND_BD_ROCK_BUGFIX    0
34 #define USE_QUICKSAND_IMPACT_BUGFIX     0
35 #define USE_DELAYED_GFX_REDRAW          0
36 #define USE_NEW_PLAYER_ASSIGNMENTS      1
37
38 #if USE_DELAYED_GFX_REDRAW
39 #define TEST_DrawLevelField(x, y)                               \
40         GfxRedraw[x][y] |= GFX_REDRAW_TILE
41 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
42         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
43 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
44         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
45 #define TEST_DrawTwinkleOnField(x, y)                           \
46         GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
47 #else
48 #define TEST_DrawLevelField(x, y)                               \
49              DrawLevelField(x, y)
50 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
51              DrawLevelFieldCrumbled(x, y)
52 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
53              DrawLevelFieldCrumbledNeighbours(x, y)
54 #define TEST_DrawTwinkleOnField(x, y)                           \
55              DrawTwinkleOnField(x, y)
56 #endif
57
58
59 // for DigField()
60 #define DF_NO_PUSH              0
61 #define DF_DIG                  1
62 #define DF_SNAP                 2
63
64 // for MovePlayer()
65 #define MP_NO_ACTION            0
66 #define MP_MOVING               1
67 #define MP_ACTION               2
68 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
69
70 // for ScrollPlayer()
71 #define SCROLL_INIT             0
72 #define SCROLL_GO_ON            1
73
74 // for Bang()/Explode()
75 #define EX_PHASE_START          0
76 #define EX_TYPE_NONE            0
77 #define EX_TYPE_NORMAL          (1 << 0)
78 #define EX_TYPE_CENTER          (1 << 1)
79 #define EX_TYPE_BORDER          (1 << 2)
80 #define EX_TYPE_CROSS           (1 << 3)
81 #define EX_TYPE_DYNA            (1 << 4)
82 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
83
84 #define PANEL_OFF()             (game.panel.active == FALSE)
85 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
86 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
87 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
88
89 // game panel display and control definitions
90 #define GAME_PANEL_LEVEL_NUMBER                 0
91 #define GAME_PANEL_GEMS                         1
92 #define GAME_PANEL_INVENTORY_COUNT              2
93 #define GAME_PANEL_INVENTORY_FIRST_1            3
94 #define GAME_PANEL_INVENTORY_FIRST_2            4
95 #define GAME_PANEL_INVENTORY_FIRST_3            5
96 #define GAME_PANEL_INVENTORY_FIRST_4            6
97 #define GAME_PANEL_INVENTORY_FIRST_5            7
98 #define GAME_PANEL_INVENTORY_FIRST_6            8
99 #define GAME_PANEL_INVENTORY_FIRST_7            9
100 #define GAME_PANEL_INVENTORY_FIRST_8            10
101 #define GAME_PANEL_INVENTORY_LAST_1             11
102 #define GAME_PANEL_INVENTORY_LAST_2             12
103 #define GAME_PANEL_INVENTORY_LAST_3             13
104 #define GAME_PANEL_INVENTORY_LAST_4             14
105 #define GAME_PANEL_INVENTORY_LAST_5             15
106 #define GAME_PANEL_INVENTORY_LAST_6             16
107 #define GAME_PANEL_INVENTORY_LAST_7             17
108 #define GAME_PANEL_INVENTORY_LAST_8             18
109 #define GAME_PANEL_KEY_1                        19
110 #define GAME_PANEL_KEY_2                        20
111 #define GAME_PANEL_KEY_3                        21
112 #define GAME_PANEL_KEY_4                        22
113 #define GAME_PANEL_KEY_5                        23
114 #define GAME_PANEL_KEY_6                        24
115 #define GAME_PANEL_KEY_7                        25
116 #define GAME_PANEL_KEY_8                        26
117 #define GAME_PANEL_KEY_WHITE                    27
118 #define GAME_PANEL_KEY_WHITE_COUNT              28
119 #define GAME_PANEL_SCORE                        29
120 #define GAME_PANEL_HIGHSCORE                    30
121 #define GAME_PANEL_TIME                         31
122 #define GAME_PANEL_TIME_HH                      32
123 #define GAME_PANEL_TIME_MM                      33
124 #define GAME_PANEL_TIME_SS                      34
125 #define GAME_PANEL_TIME_ANIM                    35
126 #define GAME_PANEL_HEALTH                       36
127 #define GAME_PANEL_HEALTH_ANIM                  37
128 #define GAME_PANEL_FRAME                        38
129 #define GAME_PANEL_SHIELD_NORMAL                39
130 #define GAME_PANEL_SHIELD_NORMAL_TIME           40
131 #define GAME_PANEL_SHIELD_DEADLY                41
132 #define GAME_PANEL_SHIELD_DEADLY_TIME           42
133 #define GAME_PANEL_EXIT                         43
134 #define GAME_PANEL_EMC_MAGIC_BALL               44
135 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        45
136 #define GAME_PANEL_LIGHT_SWITCH                 46
137 #define GAME_PANEL_LIGHT_SWITCH_TIME            47
138 #define GAME_PANEL_TIMEGATE_SWITCH              48
139 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         49
140 #define GAME_PANEL_SWITCHGATE_SWITCH            50
141 #define GAME_PANEL_EMC_LENSES                   51
142 #define GAME_PANEL_EMC_LENSES_TIME              52
143 #define GAME_PANEL_EMC_MAGNIFIER                53
144 #define GAME_PANEL_EMC_MAGNIFIER_TIME           54
145 #define GAME_PANEL_BALLOON_SWITCH               55
146 #define GAME_PANEL_DYNABOMB_NUMBER              56
147 #define GAME_PANEL_DYNABOMB_SIZE                57
148 #define GAME_PANEL_DYNABOMB_POWER               58
149 #define GAME_PANEL_PENGUINS                     59
150 #define GAME_PANEL_SOKOBAN_OBJECTS              60
151 #define GAME_PANEL_SOKOBAN_FIELDS               61
152 #define GAME_PANEL_ROBOT_WHEEL                  62
153 #define GAME_PANEL_CONVEYOR_BELT_1              63
154 #define GAME_PANEL_CONVEYOR_BELT_2              64
155 #define GAME_PANEL_CONVEYOR_BELT_3              65
156 #define GAME_PANEL_CONVEYOR_BELT_4              66
157 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       67
158 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       68
159 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       69
160 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       70
161 #define GAME_PANEL_MAGIC_WALL                   71
162 #define GAME_PANEL_MAGIC_WALL_TIME              72
163 #define GAME_PANEL_GRAVITY_STATE                73
164 #define GAME_PANEL_GRAPHIC_1                    74
165 #define GAME_PANEL_GRAPHIC_2                    75
166 #define GAME_PANEL_GRAPHIC_3                    76
167 #define GAME_PANEL_GRAPHIC_4                    77
168 #define GAME_PANEL_GRAPHIC_5                    78
169 #define GAME_PANEL_GRAPHIC_6                    79
170 #define GAME_PANEL_GRAPHIC_7                    80
171 #define GAME_PANEL_GRAPHIC_8                    81
172 #define GAME_PANEL_ELEMENT_1                    82
173 #define GAME_PANEL_ELEMENT_2                    83
174 #define GAME_PANEL_ELEMENT_3                    84
175 #define GAME_PANEL_ELEMENT_4                    85
176 #define GAME_PANEL_ELEMENT_5                    86
177 #define GAME_PANEL_ELEMENT_6                    87
178 #define GAME_PANEL_ELEMENT_7                    88
179 #define GAME_PANEL_ELEMENT_8                    89
180 #define GAME_PANEL_ELEMENT_COUNT_1              90
181 #define GAME_PANEL_ELEMENT_COUNT_2              91
182 #define GAME_PANEL_ELEMENT_COUNT_3              92
183 #define GAME_PANEL_ELEMENT_COUNT_4              93
184 #define GAME_PANEL_ELEMENT_COUNT_5              94
185 #define GAME_PANEL_ELEMENT_COUNT_6              95
186 #define GAME_PANEL_ELEMENT_COUNT_7              96
187 #define GAME_PANEL_ELEMENT_COUNT_8              97
188 #define GAME_PANEL_CE_SCORE_1                   98
189 #define GAME_PANEL_CE_SCORE_2                   99
190 #define GAME_PANEL_CE_SCORE_3                   100
191 #define GAME_PANEL_CE_SCORE_4                   101
192 #define GAME_PANEL_CE_SCORE_5                   102
193 #define GAME_PANEL_CE_SCORE_6                   103
194 #define GAME_PANEL_CE_SCORE_7                   104
195 #define GAME_PANEL_CE_SCORE_8                   105
196 #define GAME_PANEL_CE_SCORE_1_ELEMENT           106
197 #define GAME_PANEL_CE_SCORE_2_ELEMENT           107
198 #define GAME_PANEL_CE_SCORE_3_ELEMENT           108
199 #define GAME_PANEL_CE_SCORE_4_ELEMENT           109
200 #define GAME_PANEL_CE_SCORE_5_ELEMENT           110
201 #define GAME_PANEL_CE_SCORE_6_ELEMENT           111
202 #define GAME_PANEL_CE_SCORE_7_ELEMENT           112
203 #define GAME_PANEL_CE_SCORE_8_ELEMENT           113
204 #define GAME_PANEL_PLAYER_NAME                  114
205 #define GAME_PANEL_LEVEL_NAME                   115
206 #define GAME_PANEL_LEVEL_AUTHOR                 116
207
208 #define NUM_GAME_PANEL_CONTROLS                 117
209
210 struct GamePanelOrderInfo
211 {
212   int nr;
213   int sort_priority;
214 };
215
216 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
217
218 struct GamePanelControlInfo
219 {
220   int nr;
221
222   struct TextPosInfo *pos;
223   int type;
224
225   int graphic, graphic_active;
226
227   int value, last_value;
228   int frame, last_frame;
229   int gfx_frame;
230   int gfx_random;
231 };
232
233 static struct GamePanelControlInfo game_panel_controls[] =
234 {
235   {
236     GAME_PANEL_LEVEL_NUMBER,
237     &game.panel.level_number,
238     TYPE_INTEGER,
239   },
240   {
241     GAME_PANEL_GEMS,
242     &game.panel.gems,
243     TYPE_INTEGER,
244   },
245   {
246     GAME_PANEL_INVENTORY_COUNT,
247     &game.panel.inventory_count,
248     TYPE_INTEGER,
249   },
250   {
251     GAME_PANEL_INVENTORY_FIRST_1,
252     &game.panel.inventory_first[0],
253     TYPE_ELEMENT,
254   },
255   {
256     GAME_PANEL_INVENTORY_FIRST_2,
257     &game.panel.inventory_first[1],
258     TYPE_ELEMENT,
259   },
260   {
261     GAME_PANEL_INVENTORY_FIRST_3,
262     &game.panel.inventory_first[2],
263     TYPE_ELEMENT,
264   },
265   {
266     GAME_PANEL_INVENTORY_FIRST_4,
267     &game.panel.inventory_first[3],
268     TYPE_ELEMENT,
269   },
270   {
271     GAME_PANEL_INVENTORY_FIRST_5,
272     &game.panel.inventory_first[4],
273     TYPE_ELEMENT,
274   },
275   {
276     GAME_PANEL_INVENTORY_FIRST_6,
277     &game.panel.inventory_first[5],
278     TYPE_ELEMENT,
279   },
280   {
281     GAME_PANEL_INVENTORY_FIRST_7,
282     &game.panel.inventory_first[6],
283     TYPE_ELEMENT,
284   },
285   {
286     GAME_PANEL_INVENTORY_FIRST_8,
287     &game.panel.inventory_first[7],
288     TYPE_ELEMENT,
289   },
290   {
291     GAME_PANEL_INVENTORY_LAST_1,
292     &game.panel.inventory_last[0],
293     TYPE_ELEMENT,
294   },
295   {
296     GAME_PANEL_INVENTORY_LAST_2,
297     &game.panel.inventory_last[1],
298     TYPE_ELEMENT,
299   },
300   {
301     GAME_PANEL_INVENTORY_LAST_3,
302     &game.panel.inventory_last[2],
303     TYPE_ELEMENT,
304   },
305   {
306     GAME_PANEL_INVENTORY_LAST_4,
307     &game.panel.inventory_last[3],
308     TYPE_ELEMENT,
309   },
310   {
311     GAME_PANEL_INVENTORY_LAST_5,
312     &game.panel.inventory_last[4],
313     TYPE_ELEMENT,
314   },
315   {
316     GAME_PANEL_INVENTORY_LAST_6,
317     &game.panel.inventory_last[5],
318     TYPE_ELEMENT,
319   },
320   {
321     GAME_PANEL_INVENTORY_LAST_7,
322     &game.panel.inventory_last[6],
323     TYPE_ELEMENT,
324   },
325   {
326     GAME_PANEL_INVENTORY_LAST_8,
327     &game.panel.inventory_last[7],
328     TYPE_ELEMENT,
329   },
330   {
331     GAME_PANEL_KEY_1,
332     &game.panel.key[0],
333     TYPE_ELEMENT,
334   },
335   {
336     GAME_PANEL_KEY_2,
337     &game.panel.key[1],
338     TYPE_ELEMENT,
339   },
340   {
341     GAME_PANEL_KEY_3,
342     &game.panel.key[2],
343     TYPE_ELEMENT,
344   },
345   {
346     GAME_PANEL_KEY_4,
347     &game.panel.key[3],
348     TYPE_ELEMENT,
349   },
350   {
351     GAME_PANEL_KEY_5,
352     &game.panel.key[4],
353     TYPE_ELEMENT,
354   },
355   {
356     GAME_PANEL_KEY_6,
357     &game.panel.key[5],
358     TYPE_ELEMENT,
359   },
360   {
361     GAME_PANEL_KEY_7,
362     &game.panel.key[6],
363     TYPE_ELEMENT,
364   },
365   {
366     GAME_PANEL_KEY_8,
367     &game.panel.key[7],
368     TYPE_ELEMENT,
369   },
370   {
371     GAME_PANEL_KEY_WHITE,
372     &game.panel.key_white,
373     TYPE_ELEMENT,
374   },
375   {
376     GAME_PANEL_KEY_WHITE_COUNT,
377     &game.panel.key_white_count,
378     TYPE_INTEGER,
379   },
380   {
381     GAME_PANEL_SCORE,
382     &game.panel.score,
383     TYPE_INTEGER,
384   },
385   {
386     GAME_PANEL_HIGHSCORE,
387     &game.panel.highscore,
388     TYPE_INTEGER,
389   },
390   {
391     GAME_PANEL_TIME,
392     &game.panel.time,
393     TYPE_INTEGER,
394   },
395   {
396     GAME_PANEL_TIME_HH,
397     &game.panel.time_hh,
398     TYPE_INTEGER,
399   },
400   {
401     GAME_PANEL_TIME_MM,
402     &game.panel.time_mm,
403     TYPE_INTEGER,
404   },
405   {
406     GAME_PANEL_TIME_SS,
407     &game.panel.time_ss,
408     TYPE_INTEGER,
409   },
410   {
411     GAME_PANEL_TIME_ANIM,
412     &game.panel.time_anim,
413     TYPE_GRAPHIC,
414
415     IMG_GFX_GAME_PANEL_TIME_ANIM,
416     IMG_GFX_GAME_PANEL_TIME_ANIM_ACTIVE
417   },
418   {
419     GAME_PANEL_HEALTH,
420     &game.panel.health,
421     TYPE_INTEGER,
422   },
423   {
424     GAME_PANEL_HEALTH_ANIM,
425     &game.panel.health_anim,
426     TYPE_GRAPHIC,
427
428     IMG_GFX_GAME_PANEL_HEALTH_ANIM,
429     IMG_GFX_GAME_PANEL_HEALTH_ANIM_ACTIVE
430   },
431   {
432     GAME_PANEL_FRAME,
433     &game.panel.frame,
434     TYPE_INTEGER,
435   },
436   {
437     GAME_PANEL_SHIELD_NORMAL,
438     &game.panel.shield_normal,
439     TYPE_ELEMENT,
440   },
441   {
442     GAME_PANEL_SHIELD_NORMAL_TIME,
443     &game.panel.shield_normal_time,
444     TYPE_INTEGER,
445   },
446   {
447     GAME_PANEL_SHIELD_DEADLY,
448     &game.panel.shield_deadly,
449     TYPE_ELEMENT,
450   },
451   {
452     GAME_PANEL_SHIELD_DEADLY_TIME,
453     &game.panel.shield_deadly_time,
454     TYPE_INTEGER,
455   },
456   {
457     GAME_PANEL_EXIT,
458     &game.panel.exit,
459     TYPE_ELEMENT,
460   },
461   {
462     GAME_PANEL_EMC_MAGIC_BALL,
463     &game.panel.emc_magic_ball,
464     TYPE_ELEMENT,
465   },
466   {
467     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
468     &game.panel.emc_magic_ball_switch,
469     TYPE_ELEMENT,
470   },
471   {
472     GAME_PANEL_LIGHT_SWITCH,
473     &game.panel.light_switch,
474     TYPE_ELEMENT,
475   },
476   {
477     GAME_PANEL_LIGHT_SWITCH_TIME,
478     &game.panel.light_switch_time,
479     TYPE_INTEGER,
480   },
481   {
482     GAME_PANEL_TIMEGATE_SWITCH,
483     &game.panel.timegate_switch,
484     TYPE_ELEMENT,
485   },
486   {
487     GAME_PANEL_TIMEGATE_SWITCH_TIME,
488     &game.panel.timegate_switch_time,
489     TYPE_INTEGER,
490   },
491   {
492     GAME_PANEL_SWITCHGATE_SWITCH,
493     &game.panel.switchgate_switch,
494     TYPE_ELEMENT,
495   },
496   {
497     GAME_PANEL_EMC_LENSES,
498     &game.panel.emc_lenses,
499     TYPE_ELEMENT,
500   },
501   {
502     GAME_PANEL_EMC_LENSES_TIME,
503     &game.panel.emc_lenses_time,
504     TYPE_INTEGER,
505   },
506   {
507     GAME_PANEL_EMC_MAGNIFIER,
508     &game.panel.emc_magnifier,
509     TYPE_ELEMENT,
510   },
511   {
512     GAME_PANEL_EMC_MAGNIFIER_TIME,
513     &game.panel.emc_magnifier_time,
514     TYPE_INTEGER,
515   },
516   {
517     GAME_PANEL_BALLOON_SWITCH,
518     &game.panel.balloon_switch,
519     TYPE_ELEMENT,
520   },
521   {
522     GAME_PANEL_DYNABOMB_NUMBER,
523     &game.panel.dynabomb_number,
524     TYPE_INTEGER,
525   },
526   {
527     GAME_PANEL_DYNABOMB_SIZE,
528     &game.panel.dynabomb_size,
529     TYPE_INTEGER,
530   },
531   {
532     GAME_PANEL_DYNABOMB_POWER,
533     &game.panel.dynabomb_power,
534     TYPE_ELEMENT,
535   },
536   {
537     GAME_PANEL_PENGUINS,
538     &game.panel.penguins,
539     TYPE_INTEGER,
540   },
541   {
542     GAME_PANEL_SOKOBAN_OBJECTS,
543     &game.panel.sokoban_objects,
544     TYPE_INTEGER,
545   },
546   {
547     GAME_PANEL_SOKOBAN_FIELDS,
548     &game.panel.sokoban_fields,
549     TYPE_INTEGER,
550   },
551   {
552     GAME_PANEL_ROBOT_WHEEL,
553     &game.panel.robot_wheel,
554     TYPE_ELEMENT,
555   },
556   {
557     GAME_PANEL_CONVEYOR_BELT_1,
558     &game.panel.conveyor_belt[0],
559     TYPE_ELEMENT,
560   },
561   {
562     GAME_PANEL_CONVEYOR_BELT_2,
563     &game.panel.conveyor_belt[1],
564     TYPE_ELEMENT,
565   },
566   {
567     GAME_PANEL_CONVEYOR_BELT_3,
568     &game.panel.conveyor_belt[2],
569     TYPE_ELEMENT,
570   },
571   {
572     GAME_PANEL_CONVEYOR_BELT_4,
573     &game.panel.conveyor_belt[3],
574     TYPE_ELEMENT,
575   },
576   {
577     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
578     &game.panel.conveyor_belt_switch[0],
579     TYPE_ELEMENT,
580   },
581   {
582     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
583     &game.panel.conveyor_belt_switch[1],
584     TYPE_ELEMENT,
585   },
586   {
587     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
588     &game.panel.conveyor_belt_switch[2],
589     TYPE_ELEMENT,
590   },
591   {
592     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
593     &game.panel.conveyor_belt_switch[3],
594     TYPE_ELEMENT,
595   },
596   {
597     GAME_PANEL_MAGIC_WALL,
598     &game.panel.magic_wall,
599     TYPE_ELEMENT,
600   },
601   {
602     GAME_PANEL_MAGIC_WALL_TIME,
603     &game.panel.magic_wall_time,
604     TYPE_INTEGER,
605   },
606   {
607     GAME_PANEL_GRAVITY_STATE,
608     &game.panel.gravity_state,
609     TYPE_STRING,
610   },
611   {
612     GAME_PANEL_GRAPHIC_1,
613     &game.panel.graphic[0],
614     TYPE_ELEMENT,
615   },
616   {
617     GAME_PANEL_GRAPHIC_2,
618     &game.panel.graphic[1],
619     TYPE_ELEMENT,
620   },
621   {
622     GAME_PANEL_GRAPHIC_3,
623     &game.panel.graphic[2],
624     TYPE_ELEMENT,
625   },
626   {
627     GAME_PANEL_GRAPHIC_4,
628     &game.panel.graphic[3],
629     TYPE_ELEMENT,
630   },
631   {
632     GAME_PANEL_GRAPHIC_5,
633     &game.panel.graphic[4],
634     TYPE_ELEMENT,
635   },
636   {
637     GAME_PANEL_GRAPHIC_6,
638     &game.panel.graphic[5],
639     TYPE_ELEMENT,
640   },
641   {
642     GAME_PANEL_GRAPHIC_7,
643     &game.panel.graphic[6],
644     TYPE_ELEMENT,
645   },
646   {
647     GAME_PANEL_GRAPHIC_8,
648     &game.panel.graphic[7],
649     TYPE_ELEMENT,
650   },
651   {
652     GAME_PANEL_ELEMENT_1,
653     &game.panel.element[0],
654     TYPE_ELEMENT,
655   },
656   {
657     GAME_PANEL_ELEMENT_2,
658     &game.panel.element[1],
659     TYPE_ELEMENT,
660   },
661   {
662     GAME_PANEL_ELEMENT_3,
663     &game.panel.element[2],
664     TYPE_ELEMENT,
665   },
666   {
667     GAME_PANEL_ELEMENT_4,
668     &game.panel.element[3],
669     TYPE_ELEMENT,
670   },
671   {
672     GAME_PANEL_ELEMENT_5,
673     &game.panel.element[4],
674     TYPE_ELEMENT,
675   },
676   {
677     GAME_PANEL_ELEMENT_6,
678     &game.panel.element[5],
679     TYPE_ELEMENT,
680   },
681   {
682     GAME_PANEL_ELEMENT_7,
683     &game.panel.element[6],
684     TYPE_ELEMENT,
685   },
686   {
687     GAME_PANEL_ELEMENT_8,
688     &game.panel.element[7],
689     TYPE_ELEMENT,
690   },
691   {
692     GAME_PANEL_ELEMENT_COUNT_1,
693     &game.panel.element_count[0],
694     TYPE_INTEGER,
695   },
696   {
697     GAME_PANEL_ELEMENT_COUNT_2,
698     &game.panel.element_count[1],
699     TYPE_INTEGER,
700   },
701   {
702     GAME_PANEL_ELEMENT_COUNT_3,
703     &game.panel.element_count[2],
704     TYPE_INTEGER,
705   },
706   {
707     GAME_PANEL_ELEMENT_COUNT_4,
708     &game.panel.element_count[3],
709     TYPE_INTEGER,
710   },
711   {
712     GAME_PANEL_ELEMENT_COUNT_5,
713     &game.panel.element_count[4],
714     TYPE_INTEGER,
715   },
716   {
717     GAME_PANEL_ELEMENT_COUNT_6,
718     &game.panel.element_count[5],
719     TYPE_INTEGER,
720   },
721   {
722     GAME_PANEL_ELEMENT_COUNT_7,
723     &game.panel.element_count[6],
724     TYPE_INTEGER,
725   },
726   {
727     GAME_PANEL_ELEMENT_COUNT_8,
728     &game.panel.element_count[7],
729     TYPE_INTEGER,
730   },
731   {
732     GAME_PANEL_CE_SCORE_1,
733     &game.panel.ce_score[0],
734     TYPE_INTEGER,
735   },
736   {
737     GAME_PANEL_CE_SCORE_2,
738     &game.panel.ce_score[1],
739     TYPE_INTEGER,
740   },
741   {
742     GAME_PANEL_CE_SCORE_3,
743     &game.panel.ce_score[2],
744     TYPE_INTEGER,
745   },
746   {
747     GAME_PANEL_CE_SCORE_4,
748     &game.panel.ce_score[3],
749     TYPE_INTEGER,
750   },
751   {
752     GAME_PANEL_CE_SCORE_5,
753     &game.panel.ce_score[4],
754     TYPE_INTEGER,
755   },
756   {
757     GAME_PANEL_CE_SCORE_6,
758     &game.panel.ce_score[5],
759     TYPE_INTEGER,
760   },
761   {
762     GAME_PANEL_CE_SCORE_7,
763     &game.panel.ce_score[6],
764     TYPE_INTEGER,
765   },
766   {
767     GAME_PANEL_CE_SCORE_8,
768     &game.panel.ce_score[7],
769     TYPE_INTEGER,
770   },
771   {
772     GAME_PANEL_CE_SCORE_1_ELEMENT,
773     &game.panel.ce_score_element[0],
774     TYPE_ELEMENT,
775   },
776   {
777     GAME_PANEL_CE_SCORE_2_ELEMENT,
778     &game.panel.ce_score_element[1],
779     TYPE_ELEMENT,
780   },
781   {
782     GAME_PANEL_CE_SCORE_3_ELEMENT,
783     &game.panel.ce_score_element[2],
784     TYPE_ELEMENT,
785   },
786   {
787     GAME_PANEL_CE_SCORE_4_ELEMENT,
788     &game.panel.ce_score_element[3],
789     TYPE_ELEMENT,
790   },
791   {
792     GAME_PANEL_CE_SCORE_5_ELEMENT,
793     &game.panel.ce_score_element[4],
794     TYPE_ELEMENT,
795   },
796   {
797     GAME_PANEL_CE_SCORE_6_ELEMENT,
798     &game.panel.ce_score_element[5],
799     TYPE_ELEMENT,
800   },
801   {
802     GAME_PANEL_CE_SCORE_7_ELEMENT,
803     &game.panel.ce_score_element[6],
804     TYPE_ELEMENT,
805   },
806   {
807     GAME_PANEL_CE_SCORE_8_ELEMENT,
808     &game.panel.ce_score_element[7],
809     TYPE_ELEMENT,
810   },
811   {
812     GAME_PANEL_PLAYER_NAME,
813     &game.panel.player_name,
814     TYPE_STRING,
815   },
816   {
817     GAME_PANEL_LEVEL_NAME,
818     &game.panel.level_name,
819     TYPE_STRING,
820   },
821   {
822     GAME_PANEL_LEVEL_AUTHOR,
823     &game.panel.level_author,
824     TYPE_STRING,
825   },
826
827   {
828     -1,
829     NULL,
830     -1,
831   }
832 };
833
834 // values for delayed check of falling and moving elements and for collision
835 #define CHECK_DELAY_MOVING      3
836 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
837 #define CHECK_DELAY_COLLISION   2
838 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
839
840 // values for initial player move delay (initial delay counter value)
841 #define INITIAL_MOVE_DELAY_OFF  -1
842 #define INITIAL_MOVE_DELAY_ON   0
843
844 // values for player movement speed (which is in fact a delay value)
845 #define MOVE_DELAY_MIN_SPEED    32
846 #define MOVE_DELAY_NORMAL_SPEED 8
847 #define MOVE_DELAY_HIGH_SPEED   4
848 #define MOVE_DELAY_MAX_SPEED    1
849
850 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
851 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
852
853 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
854 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
855
856 // values for scroll positions
857 #define SCROLL_POSITION_X(x)    ((x) < SBX_Left  + MIDPOSX ? SBX_Left : \
858                                  (x) > SBX_Right + MIDPOSX ? SBX_Right :\
859                                  (x) - MIDPOSX)
860 #define SCROLL_POSITION_Y(y)    ((y) < SBY_Upper + MIDPOSY ? SBY_Upper :\
861                                  (y) > SBY_Lower + MIDPOSY ? SBY_Lower :\
862                                  (y) - MIDPOSY)
863
864 // values for other actions
865 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
866 #define MOVE_STEPSIZE_MIN       (1)
867 #define MOVE_STEPSIZE_MAX       (TILEX)
868
869 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
870 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
871
872 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
873
874 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
875                                  RND(element_info[e].push_delay_random))
876 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
877                                  RND(element_info[e].drop_delay_random))
878 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
879                                  RND(element_info[e].move_delay_random))
880 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
881                                     (element_info[e].move_delay_random))
882 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
883                                  RND(element_info[e].ce_value_random_initial))
884 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
885 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
886                                  RND((c)->delay_random * (c)->delay_frames))
887 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
888                                  RND((c)->delay_random))
889
890
891 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
892          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
893
894 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
895         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
896          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
897          (be) + (e) - EL_SELF)
898
899 #define GET_PLAYER_FROM_BITS(p)                                         \
900         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
901
902 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
903         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
904          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
905          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
906          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
907          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
908          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
909          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
910          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
911          (e))
912
913 #define CAN_GROW_INTO(e)                                                \
914         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
915
916 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
917                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
918                                         (condition)))
919
920 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
921                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
922                                         (CAN_MOVE_INTO_ACID(e) &&       \
923                                          Feld[x][y] == EL_ACID) ||      \
924                                         (condition)))
925
926 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
927                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
928                                         (CAN_MOVE_INTO_ACID(e) &&       \
929                                          Feld[x][y] == EL_ACID) ||      \
930                                         (condition)))
931
932 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
933                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
934                                         (condition) ||                  \
935                                         (CAN_MOVE_INTO_ACID(e) &&       \
936                                          Feld[x][y] == EL_ACID) ||      \
937                                         (DONT_COLLIDE_WITH(e) &&        \
938                                          IS_PLAYER(x, y) &&             \
939                                          !PLAYER_ENEMY_PROTECTED(x, y))))
940
941 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
942         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
943
944 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
945         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
946
947 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
948         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
949
950 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
951         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
952                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
953
954 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
955         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
956
957 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
958         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
959
960 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
961         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
962
963 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
964         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
965
966 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
967         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
968
969 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
970         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
971                                                  Feld[x][y] == EL_EM_EXIT_OPEN || \
972                                                  Feld[x][y] == EL_STEEL_EXIT_OPEN || \
973                                                  Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
974                                                  IS_FOOD_PENGUIN(Feld[x][y])))
975 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
976         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
977
978 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
979         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
980
981 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
982         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
983
984 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
985         (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER ||   \
986                                 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
987
988 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
989
990 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
991                 (!IS_PLAYER(x, y) &&                                    \
992                  IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
993
994 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
995         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
996
997 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
998 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
999
1000 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
1001 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
1002 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
1003 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1004
1005 #define MM_HEALTH(x)            (MIN(MAX(0, MAX_HEALTH - (x)), MAX_HEALTH))
1006
1007 // game button identifiers
1008 #define GAME_CTRL_ID_STOP               0
1009 #define GAME_CTRL_ID_PAUSE              1
1010 #define GAME_CTRL_ID_PLAY               2
1011 #define GAME_CTRL_ID_UNDO               3
1012 #define GAME_CTRL_ID_REDO               4
1013 #define GAME_CTRL_ID_SAVE               5
1014 #define GAME_CTRL_ID_PAUSE2             6
1015 #define GAME_CTRL_ID_LOAD               7
1016 #define GAME_CTRL_ID_PANEL_STOP         8
1017 #define GAME_CTRL_ID_PANEL_PAUSE        9
1018 #define GAME_CTRL_ID_PANEL_PLAY         10
1019 #define GAME_CTRL_ID_TOUCH_STOP         11
1020 #define GAME_CTRL_ID_TOUCH_PAUSE        12
1021 #define SOUND_CTRL_ID_MUSIC             13
1022 #define SOUND_CTRL_ID_LOOPS             14
1023 #define SOUND_CTRL_ID_SIMPLE            15
1024 #define SOUND_CTRL_ID_PANEL_MUSIC       16
1025 #define SOUND_CTRL_ID_PANEL_LOOPS       17
1026 #define SOUND_CTRL_ID_PANEL_SIMPLE      18
1027
1028 #define NUM_GAME_BUTTONS                19
1029
1030
1031 // forward declaration for internal use
1032
1033 static void CreateField(int, int, int);
1034
1035 static void ResetGfxAnimation(int, int);
1036
1037 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1038 static void AdvanceFrameAndPlayerCounters(int);
1039
1040 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1041 static boolean MovePlayer(struct PlayerInfo *, int, int);
1042 static void ScrollPlayer(struct PlayerInfo *, int);
1043 static void ScrollScreen(struct PlayerInfo *, int);
1044
1045 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1046 static boolean DigFieldByCE(int, int, int);
1047 static boolean SnapField(struct PlayerInfo *, int, int);
1048 static boolean DropElement(struct PlayerInfo *);
1049
1050 static void InitBeltMovement(void);
1051 static void CloseAllOpenTimegates(void);
1052 static void CheckGravityMovement(struct PlayerInfo *);
1053 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1054 static void KillPlayerUnlessEnemyProtected(int, int);
1055 static void KillPlayerUnlessExplosionProtected(int, int);
1056
1057 static void TestIfPlayerTouchesCustomElement(int, int);
1058 static void TestIfElementTouchesCustomElement(int, int);
1059 static void TestIfElementHitsCustomElement(int, int, int);
1060
1061 static void HandleElementChange(int, int, int);
1062 static void ExecuteCustomElementAction(int, int, int, int);
1063 static boolean ChangeElement(int, int, int, int);
1064
1065 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1066 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1067         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1068 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1069         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1070 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1071         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1072 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1073         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1074
1075 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1076 #define CheckElementChange(x, y, e, te, ev)                             \
1077         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1078 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1079         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1080 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1081         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1082
1083 static void PlayLevelSound(int, int, int);
1084 static void PlayLevelSoundNearest(int, int, int);
1085 static void PlayLevelSoundAction(int, int, int);
1086 static void PlayLevelSoundElementAction(int, int, int, int);
1087 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1088 static void PlayLevelSoundActionIfLoop(int, int, int);
1089 static void StopLevelSoundActionIfLoop(int, int, int);
1090 static void PlayLevelMusic(void);
1091 static void FadeLevelSoundsAndMusic(void);
1092
1093 static void HandleGameButtons(struct GadgetInfo *);
1094
1095 int AmoebeNachbarNr(int, int);
1096 void AmoebeUmwandeln(int, int);
1097 void ContinueMoving(int, int);
1098 void Bang(int, int);
1099 void InitMovDir(int, int);
1100 void InitAmoebaNr(int, int);
1101 int NewHiScore(int);
1102
1103 void TestIfGoodThingHitsBadThing(int, int, int);
1104 void TestIfBadThingHitsGoodThing(int, int, int);
1105 void TestIfPlayerTouchesBadThing(int, int);
1106 void TestIfPlayerRunsIntoBadThing(int, int, int);
1107 void TestIfBadThingTouchesPlayer(int, int);
1108 void TestIfBadThingRunsIntoPlayer(int, int, int);
1109 void TestIfFriendTouchesBadThing(int, int);
1110 void TestIfBadThingTouchesFriend(int, int);
1111 void TestIfBadThingTouchesOtherBadThing(int, int);
1112 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1113
1114 void KillPlayer(struct PlayerInfo *);
1115 void BuryPlayer(struct PlayerInfo *);
1116 void RemovePlayer(struct PlayerInfo *);
1117 void ExitPlayer(struct PlayerInfo *);
1118
1119 static int getInvisibleActiveFromInvisibleElement(int);
1120 static int getInvisibleFromInvisibleActiveElement(int);
1121
1122 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1123
1124 // for detection of endless loops, caused by custom element programming
1125 // (using maximal playfield width x 10 is just a rough approximation)
1126 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1127
1128 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1129 {                                                                       \
1130   if (recursion_loop_detected)                                          \
1131     return (rc);                                                        \
1132                                                                         \
1133   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1134   {                                                                     \
1135     recursion_loop_detected = TRUE;                                     \
1136     recursion_loop_element = (e);                                       \
1137   }                                                                     \
1138                                                                         \
1139   recursion_loop_depth++;                                               \
1140 }
1141
1142 #define RECURSION_LOOP_DETECTION_END()                                  \
1143 {                                                                       \
1144   recursion_loop_depth--;                                               \
1145 }
1146
1147 static int recursion_loop_depth;
1148 static boolean recursion_loop_detected;
1149 static boolean recursion_loop_element;
1150
1151 static int map_player_action[MAX_PLAYERS];
1152
1153
1154 // ----------------------------------------------------------------------------
1155 // definition of elements that automatically change to other elements after
1156 // a specified time, eventually calling a function when changing
1157 // ----------------------------------------------------------------------------
1158
1159 // forward declaration for changer functions
1160 static void InitBuggyBase(int, int);
1161 static void WarnBuggyBase(int, int);
1162
1163 static void InitTrap(int, int);
1164 static void ActivateTrap(int, int);
1165 static void ChangeActiveTrap(int, int);
1166
1167 static void InitRobotWheel(int, int);
1168 static void RunRobotWheel(int, int);
1169 static void StopRobotWheel(int, int);
1170
1171 static void InitTimegateWheel(int, int);
1172 static void RunTimegateWheel(int, int);
1173
1174 static void InitMagicBallDelay(int, int);
1175 static void ActivateMagicBall(int, int);
1176
1177 struct ChangingElementInfo
1178 {
1179   int element;
1180   int target_element;
1181   int change_delay;
1182   void (*pre_change_function)(int x, int y);
1183   void (*change_function)(int x, int y);
1184   void (*post_change_function)(int x, int y);
1185 };
1186
1187 static struct ChangingElementInfo change_delay_list[] =
1188 {
1189   {
1190     EL_NUT_BREAKING,
1191     EL_EMERALD,
1192     6,
1193     NULL,
1194     NULL,
1195     NULL
1196   },
1197   {
1198     EL_PEARL_BREAKING,
1199     EL_EMPTY,
1200     8,
1201     NULL,
1202     NULL,
1203     NULL
1204   },
1205   {
1206     EL_EXIT_OPENING,
1207     EL_EXIT_OPEN,
1208     29,
1209     NULL,
1210     NULL,
1211     NULL
1212   },
1213   {
1214     EL_EXIT_CLOSING,
1215     EL_EXIT_CLOSED,
1216     29,
1217     NULL,
1218     NULL,
1219     NULL
1220   },
1221   {
1222     EL_STEEL_EXIT_OPENING,
1223     EL_STEEL_EXIT_OPEN,
1224     29,
1225     NULL,
1226     NULL,
1227     NULL
1228   },
1229   {
1230     EL_STEEL_EXIT_CLOSING,
1231     EL_STEEL_EXIT_CLOSED,
1232     29,
1233     NULL,
1234     NULL,
1235     NULL
1236   },
1237   {
1238     EL_EM_EXIT_OPENING,
1239     EL_EM_EXIT_OPEN,
1240     29,
1241     NULL,
1242     NULL,
1243     NULL
1244   },
1245   {
1246     EL_EM_EXIT_CLOSING,
1247     EL_EMPTY,
1248     29,
1249     NULL,
1250     NULL,
1251     NULL
1252   },
1253   {
1254     EL_EM_STEEL_EXIT_OPENING,
1255     EL_EM_STEEL_EXIT_OPEN,
1256     29,
1257     NULL,
1258     NULL,
1259     NULL
1260   },
1261   {
1262     EL_EM_STEEL_EXIT_CLOSING,
1263     EL_STEELWALL,
1264     29,
1265     NULL,
1266     NULL,
1267     NULL
1268   },
1269   {
1270     EL_SP_EXIT_OPENING,
1271     EL_SP_EXIT_OPEN,
1272     29,
1273     NULL,
1274     NULL,
1275     NULL
1276   },
1277   {
1278     EL_SP_EXIT_CLOSING,
1279     EL_SP_EXIT_CLOSED,
1280     29,
1281     NULL,
1282     NULL,
1283     NULL
1284   },
1285   {
1286     EL_SWITCHGATE_OPENING,
1287     EL_SWITCHGATE_OPEN,
1288     29,
1289     NULL,
1290     NULL,
1291     NULL
1292   },
1293   {
1294     EL_SWITCHGATE_CLOSING,
1295     EL_SWITCHGATE_CLOSED,
1296     29,
1297     NULL,
1298     NULL,
1299     NULL
1300   },
1301   {
1302     EL_TIMEGATE_OPENING,
1303     EL_TIMEGATE_OPEN,
1304     29,
1305     NULL,
1306     NULL,
1307     NULL
1308   },
1309   {
1310     EL_TIMEGATE_CLOSING,
1311     EL_TIMEGATE_CLOSED,
1312     29,
1313     NULL,
1314     NULL,
1315     NULL
1316   },
1317
1318   {
1319     EL_ACID_SPLASH_LEFT,
1320     EL_EMPTY,
1321     8,
1322     NULL,
1323     NULL,
1324     NULL
1325   },
1326   {
1327     EL_ACID_SPLASH_RIGHT,
1328     EL_EMPTY,
1329     8,
1330     NULL,
1331     NULL,
1332     NULL
1333   },
1334   {
1335     EL_SP_BUGGY_BASE,
1336     EL_SP_BUGGY_BASE_ACTIVATING,
1337     0,
1338     InitBuggyBase,
1339     NULL,
1340     NULL
1341   },
1342   {
1343     EL_SP_BUGGY_BASE_ACTIVATING,
1344     EL_SP_BUGGY_BASE_ACTIVE,
1345     0,
1346     InitBuggyBase,
1347     NULL,
1348     NULL
1349   },
1350   {
1351     EL_SP_BUGGY_BASE_ACTIVE,
1352     EL_SP_BUGGY_BASE,
1353     0,
1354     InitBuggyBase,
1355     WarnBuggyBase,
1356     NULL
1357   },
1358   {
1359     EL_TRAP,
1360     EL_TRAP_ACTIVE,
1361     0,
1362     InitTrap,
1363     NULL,
1364     ActivateTrap
1365   },
1366   {
1367     EL_TRAP_ACTIVE,
1368     EL_TRAP,
1369     31,
1370     NULL,
1371     ChangeActiveTrap,
1372     NULL
1373   },
1374   {
1375     EL_ROBOT_WHEEL_ACTIVE,
1376     EL_ROBOT_WHEEL,
1377     0,
1378     InitRobotWheel,
1379     RunRobotWheel,
1380     StopRobotWheel
1381   },
1382   {
1383     EL_TIMEGATE_SWITCH_ACTIVE,
1384     EL_TIMEGATE_SWITCH,
1385     0,
1386     InitTimegateWheel,
1387     RunTimegateWheel,
1388     NULL
1389   },
1390   {
1391     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1392     EL_DC_TIMEGATE_SWITCH,
1393     0,
1394     InitTimegateWheel,
1395     RunTimegateWheel,
1396     NULL
1397   },
1398   {
1399     EL_EMC_MAGIC_BALL_ACTIVE,
1400     EL_EMC_MAGIC_BALL_ACTIVE,
1401     0,
1402     InitMagicBallDelay,
1403     NULL,
1404     ActivateMagicBall
1405   },
1406   {
1407     EL_EMC_SPRING_BUMPER_ACTIVE,
1408     EL_EMC_SPRING_BUMPER,
1409     8,
1410     NULL,
1411     NULL,
1412     NULL
1413   },
1414   {
1415     EL_DIAGONAL_SHRINKING,
1416     EL_UNDEFINED,
1417     0,
1418     NULL,
1419     NULL,
1420     NULL
1421   },
1422   {
1423     EL_DIAGONAL_GROWING,
1424     EL_UNDEFINED,
1425     0,
1426     NULL,
1427     NULL,
1428     NULL,
1429   },
1430
1431   {
1432     EL_UNDEFINED,
1433     EL_UNDEFINED,
1434     -1,
1435     NULL,
1436     NULL,
1437     NULL
1438   }
1439 };
1440
1441 struct
1442 {
1443   int element;
1444   int push_delay_fixed, push_delay_random;
1445 }
1446 push_delay_list[] =
1447 {
1448   { EL_SPRING,                  0, 0 },
1449   { EL_BALLOON,                 0, 0 },
1450
1451   { EL_SOKOBAN_OBJECT,          2, 0 },
1452   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1453   { EL_SATELLITE,               2, 0 },
1454   { EL_SP_DISK_YELLOW,          2, 0 },
1455
1456   { EL_UNDEFINED,               0, 0 },
1457 };
1458
1459 struct
1460 {
1461   int element;
1462   int move_stepsize;
1463 }
1464 move_stepsize_list[] =
1465 {
1466   { EL_AMOEBA_DROP,             2 },
1467   { EL_AMOEBA_DROPPING,         2 },
1468   { EL_QUICKSAND_FILLING,       1 },
1469   { EL_QUICKSAND_EMPTYING,      1 },
1470   { EL_QUICKSAND_FAST_FILLING,  2 },
1471   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1472   { EL_MAGIC_WALL_FILLING,      2 },
1473   { EL_MAGIC_WALL_EMPTYING,     2 },
1474   { EL_BD_MAGIC_WALL_FILLING,   2 },
1475   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1476   { EL_DC_MAGIC_WALL_FILLING,   2 },
1477   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1478
1479   { EL_UNDEFINED,               0 },
1480 };
1481
1482 struct
1483 {
1484   int element;
1485   int count;
1486 }
1487 collect_count_list[] =
1488 {
1489   { EL_EMERALD,                 1 },
1490   { EL_BD_DIAMOND,              1 },
1491   { EL_EMERALD_YELLOW,          1 },
1492   { EL_EMERALD_RED,             1 },
1493   { EL_EMERALD_PURPLE,          1 },
1494   { EL_DIAMOND,                 3 },
1495   { EL_SP_INFOTRON,             1 },
1496   { EL_PEARL,                   5 },
1497   { EL_CRYSTAL,                 8 },
1498
1499   { EL_UNDEFINED,               0 },
1500 };
1501
1502 struct
1503 {
1504   int element;
1505   int direction;
1506 }
1507 access_direction_list[] =
1508 {
1509   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1510   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1511   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1512   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1513   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1514   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1515   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1516   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1517   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1518   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1519   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1520
1521   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1522   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1523   { EL_SP_PORT_UP,                                                   MV_DOWN },
1524   { EL_SP_PORT_DOWN,                                         MV_UP           },
1525   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1526   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1527   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1528   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1529   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1530   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1531   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1532   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1533   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1534   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1535   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1536   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1537   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1538   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1539   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1540
1541   { EL_UNDEFINED,                       MV_NONE                              }
1542 };
1543
1544 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1545
1546 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1547 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1548 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1549                                  IS_JUST_CHANGING(x, y))
1550
1551 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1552
1553 // static variables for playfield scan mode (scanning forward or backward)
1554 static int playfield_scan_start_x = 0;
1555 static int playfield_scan_start_y = 0;
1556 static int playfield_scan_delta_x = 1;
1557 static int playfield_scan_delta_y = 1;
1558
1559 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1560                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1561                                      (y) += playfield_scan_delta_y)     \
1562                                 for ((x) = playfield_scan_start_x;      \
1563                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1564                                      (x) += playfield_scan_delta_x)
1565
1566 #ifdef DEBUG
1567 void DEBUG_SetMaximumDynamite(void)
1568 {
1569   int i;
1570
1571   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1572     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1573       local_player->inventory_element[local_player->inventory_size++] =
1574         EL_DYNAMITE;
1575 }
1576 #endif
1577
1578 static void InitPlayfieldScanModeVars(void)
1579 {
1580   if (game.use_reverse_scan_direction)
1581   {
1582     playfield_scan_start_x = lev_fieldx - 1;
1583     playfield_scan_start_y = lev_fieldy - 1;
1584
1585     playfield_scan_delta_x = -1;
1586     playfield_scan_delta_y = -1;
1587   }
1588   else
1589   {
1590     playfield_scan_start_x = 0;
1591     playfield_scan_start_y = 0;
1592
1593     playfield_scan_delta_x = 1;
1594     playfield_scan_delta_y = 1;
1595   }
1596 }
1597
1598 static void InitPlayfieldScanMode(int mode)
1599 {
1600   game.use_reverse_scan_direction =
1601     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1602
1603   InitPlayfieldScanModeVars();
1604 }
1605
1606 static int get_move_delay_from_stepsize(int move_stepsize)
1607 {
1608   move_stepsize =
1609     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1610
1611   // make sure that stepsize value is always a power of 2
1612   move_stepsize = (1 << log_2(move_stepsize));
1613
1614   return TILEX / move_stepsize;
1615 }
1616
1617 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1618                                boolean init_game)
1619 {
1620   int player_nr = player->index_nr;
1621   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1622   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1623
1624   // do no immediately change move delay -- the player might just be moving
1625   player->move_delay_value_next = move_delay;
1626
1627   // information if player can move must be set separately
1628   player->cannot_move = cannot_move;
1629
1630   if (init_game)
1631   {
1632     player->move_delay       = game.initial_move_delay[player_nr];
1633     player->move_delay_value = game.initial_move_delay_value[player_nr];
1634
1635     player->move_delay_value_next = -1;
1636
1637     player->move_delay_reset_counter = 0;
1638   }
1639 }
1640
1641 void GetPlayerConfig(void)
1642 {
1643   GameFrameDelay = setup.game_frame_delay;
1644
1645   if (!audio.sound_available)
1646     setup.sound_simple = FALSE;
1647
1648   if (!audio.loops_available)
1649     setup.sound_loops = FALSE;
1650
1651   if (!audio.music_available)
1652     setup.sound_music = FALSE;
1653
1654   if (!video.fullscreen_available)
1655     setup.fullscreen = FALSE;
1656
1657   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1658
1659   SetAudioMode(setup.sound);
1660 }
1661
1662 int GetElementFromGroupElement(int element)
1663 {
1664   if (IS_GROUP_ELEMENT(element))
1665   {
1666     struct ElementGroupInfo *group = element_info[element].group;
1667     int last_anim_random_frame = gfx.anim_random_frame;
1668     int element_pos;
1669
1670     if (group->choice_mode == ANIM_RANDOM)
1671       gfx.anim_random_frame = RND(group->num_elements_resolved);
1672
1673     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1674                                     group->choice_mode, 0,
1675                                     group->choice_pos);
1676
1677     if (group->choice_mode == ANIM_RANDOM)
1678       gfx.anim_random_frame = last_anim_random_frame;
1679
1680     group->choice_pos++;
1681
1682     element = group->element_resolved[element_pos];
1683   }
1684
1685   return element;
1686 }
1687
1688 static void IncrementSokobanFieldsNeeded(void)
1689 {
1690   if (level.sb_fields_needed)
1691     game.sokoban_fields_still_needed++;
1692 }
1693
1694 static void IncrementSokobanObjectsNeeded(void)
1695 {
1696   if (level.sb_objects_needed)
1697     game.sokoban_objects_still_needed++;
1698 }
1699
1700 static void DecrementSokobanFieldsNeeded(void)
1701 {
1702   if (game.sokoban_fields_still_needed > 0)
1703     game.sokoban_fields_still_needed--;
1704 }
1705
1706 static void DecrementSokobanObjectsNeeded(void)
1707 {
1708   if (game.sokoban_objects_still_needed > 0)
1709     game.sokoban_objects_still_needed--;
1710 }
1711
1712 static void InitPlayerField(int x, int y, int element, boolean init_game)
1713 {
1714   if (element == EL_SP_MURPHY)
1715   {
1716     if (init_game)
1717     {
1718       if (stored_player[0].present)
1719       {
1720         Feld[x][y] = EL_SP_MURPHY_CLONE;
1721
1722         return;
1723       }
1724       else
1725       {
1726         stored_player[0].initial_element = element;
1727         stored_player[0].use_murphy = TRUE;
1728
1729         if (!level.use_artwork_element[0])
1730           stored_player[0].artwork_element = EL_SP_MURPHY;
1731       }
1732
1733       Feld[x][y] = EL_PLAYER_1;
1734     }
1735   }
1736
1737   if (init_game)
1738   {
1739     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1740     int jx = player->jx, jy = player->jy;
1741
1742     player->present = TRUE;
1743
1744     player->block_last_field = (element == EL_SP_MURPHY ?
1745                                 level.sp_block_last_field :
1746                                 level.block_last_field);
1747
1748     // ---------- initialize player's last field block delay ------------------
1749
1750     // always start with reliable default value (no adjustment needed)
1751     player->block_delay_adjustment = 0;
1752
1753     // special case 1: in Supaplex, Murphy blocks last field one more frame
1754     if (player->block_last_field && element == EL_SP_MURPHY)
1755       player->block_delay_adjustment = 1;
1756
1757     // special case 2: in game engines before 3.1.1, blocking was different
1758     if (game.use_block_last_field_bug)
1759       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1760
1761     if (!network.enabled || player->connected_network)
1762     {
1763       player->active = TRUE;
1764
1765       // remove potentially duplicate players
1766       if (StorePlayer[jx][jy] == Feld[x][y])
1767         StorePlayer[jx][jy] = 0;
1768
1769       StorePlayer[x][y] = Feld[x][y];
1770
1771 #if DEBUG_INIT_PLAYER
1772       if (options.debug)
1773       {
1774         printf("- player element %d activated", player->element_nr);
1775         printf(" (local player is %d and currently %s)\n",
1776                local_player->element_nr,
1777                local_player->active ? "active" : "not active");
1778       }
1779     }
1780 #endif
1781
1782     Feld[x][y] = EL_EMPTY;
1783
1784     player->jx = player->last_jx = x;
1785     player->jy = player->last_jy = y;
1786   }
1787
1788   if (!init_game)
1789   {
1790     int player_nr = GET_PLAYER_NR(element);
1791     struct PlayerInfo *player = &stored_player[player_nr];
1792
1793     if (player->active && player->killed)
1794       player->reanimated = TRUE; // if player was just killed, reanimate him
1795   }
1796 }
1797
1798 static void InitField(int x, int y, boolean init_game)
1799 {
1800   int element = Feld[x][y];
1801
1802   switch (element)
1803   {
1804     case EL_SP_MURPHY:
1805     case EL_PLAYER_1:
1806     case EL_PLAYER_2:
1807     case EL_PLAYER_3:
1808     case EL_PLAYER_4:
1809       InitPlayerField(x, y, element, init_game);
1810       break;
1811
1812     case EL_SOKOBAN_FIELD_PLAYER:
1813       element = Feld[x][y] = EL_PLAYER_1;
1814       InitField(x, y, init_game);
1815
1816       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1817       InitField(x, y, init_game);
1818       break;
1819
1820     case EL_SOKOBAN_FIELD_EMPTY:
1821       IncrementSokobanFieldsNeeded();
1822       break;
1823
1824     case EL_SOKOBAN_OBJECT:
1825       IncrementSokobanObjectsNeeded();
1826       break;
1827
1828     case EL_STONEBLOCK:
1829       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1830         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1831       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1832         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1833       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1834         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1835       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1836         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1837       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1838         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1839       break;
1840
1841     case EL_BUG:
1842     case EL_BUG_RIGHT:
1843     case EL_BUG_UP:
1844     case EL_BUG_LEFT:
1845     case EL_BUG_DOWN:
1846     case EL_SPACESHIP:
1847     case EL_SPACESHIP_RIGHT:
1848     case EL_SPACESHIP_UP:
1849     case EL_SPACESHIP_LEFT:
1850     case EL_SPACESHIP_DOWN:
1851     case EL_BD_BUTTERFLY:
1852     case EL_BD_BUTTERFLY_RIGHT:
1853     case EL_BD_BUTTERFLY_UP:
1854     case EL_BD_BUTTERFLY_LEFT:
1855     case EL_BD_BUTTERFLY_DOWN:
1856     case EL_BD_FIREFLY:
1857     case EL_BD_FIREFLY_RIGHT:
1858     case EL_BD_FIREFLY_UP:
1859     case EL_BD_FIREFLY_LEFT:
1860     case EL_BD_FIREFLY_DOWN:
1861     case EL_PACMAN_RIGHT:
1862     case EL_PACMAN_UP:
1863     case EL_PACMAN_LEFT:
1864     case EL_PACMAN_DOWN:
1865     case EL_YAMYAM:
1866     case EL_YAMYAM_LEFT:
1867     case EL_YAMYAM_RIGHT:
1868     case EL_YAMYAM_UP:
1869     case EL_YAMYAM_DOWN:
1870     case EL_DARK_YAMYAM:
1871     case EL_ROBOT:
1872     case EL_PACMAN:
1873     case EL_SP_SNIKSNAK:
1874     case EL_SP_ELECTRON:
1875     case EL_MOLE:
1876     case EL_MOLE_LEFT:
1877     case EL_MOLE_RIGHT:
1878     case EL_MOLE_UP:
1879     case EL_MOLE_DOWN:
1880       InitMovDir(x, y);
1881       break;
1882
1883     case EL_AMOEBA_FULL:
1884     case EL_BD_AMOEBA:
1885       InitAmoebaNr(x, y);
1886       break;
1887
1888     case EL_AMOEBA_DROP:
1889       if (y == lev_fieldy - 1)
1890       {
1891         Feld[x][y] = EL_AMOEBA_GROWING;
1892         Store[x][y] = EL_AMOEBA_WET;
1893       }
1894       break;
1895
1896     case EL_DYNAMITE_ACTIVE:
1897     case EL_SP_DISK_RED_ACTIVE:
1898     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1899     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1900     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1901     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1902       MovDelay[x][y] = 96;
1903       break;
1904
1905     case EL_EM_DYNAMITE_ACTIVE:
1906       MovDelay[x][y] = 32;
1907       break;
1908
1909     case EL_LAMP:
1910       game.lights_still_needed++;
1911       break;
1912
1913     case EL_PENGUIN:
1914       game.friends_still_needed++;
1915       break;
1916
1917     case EL_PIG:
1918     case EL_DRAGON:
1919       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1920       break;
1921
1922     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1923     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1924     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1925     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1926     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1927     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1928     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1929     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1930     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1931     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1932     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1933     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1934       if (init_game)
1935       {
1936         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1937         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1938         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1939
1940         if (game.belt_dir_nr[belt_nr] == 3)     // initial value
1941         {
1942           game.belt_dir[belt_nr] = belt_dir;
1943           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1944         }
1945         else    // more than one switch -- set it like the first switch
1946         {
1947           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1948         }
1949       }
1950       break;
1951
1952     case EL_LIGHT_SWITCH_ACTIVE:
1953       if (init_game)
1954         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1955       break;
1956
1957     case EL_INVISIBLE_STEELWALL:
1958     case EL_INVISIBLE_WALL:
1959     case EL_INVISIBLE_SAND:
1960       if (game.light_time_left > 0 ||
1961           game.lenses_time_left > 0)
1962         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1963       break;
1964
1965     case EL_EMC_MAGIC_BALL:
1966       if (game.ball_state)
1967         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1968       break;
1969
1970     case EL_EMC_MAGIC_BALL_SWITCH:
1971       if (game.ball_state)
1972         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1973       break;
1974
1975     case EL_TRIGGER_PLAYER:
1976     case EL_TRIGGER_ELEMENT:
1977     case EL_TRIGGER_CE_VALUE:
1978     case EL_TRIGGER_CE_SCORE:
1979     case EL_SELF:
1980     case EL_ANY_ELEMENT:
1981     case EL_CURRENT_CE_VALUE:
1982     case EL_CURRENT_CE_SCORE:
1983     case EL_PREV_CE_1:
1984     case EL_PREV_CE_2:
1985     case EL_PREV_CE_3:
1986     case EL_PREV_CE_4:
1987     case EL_PREV_CE_5:
1988     case EL_PREV_CE_6:
1989     case EL_PREV_CE_7:
1990     case EL_PREV_CE_8:
1991     case EL_NEXT_CE_1:
1992     case EL_NEXT_CE_2:
1993     case EL_NEXT_CE_3:
1994     case EL_NEXT_CE_4:
1995     case EL_NEXT_CE_5:
1996     case EL_NEXT_CE_6:
1997     case EL_NEXT_CE_7:
1998     case EL_NEXT_CE_8:
1999       // reference elements should not be used on the playfield
2000       Feld[x][y] = EL_EMPTY;
2001       break;
2002
2003     default:
2004       if (IS_CUSTOM_ELEMENT(element))
2005       {
2006         if (CAN_MOVE(element))
2007           InitMovDir(x, y);
2008
2009         if (!element_info[element].use_last_ce_value || init_game)
2010           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
2011       }
2012       else if (IS_GROUP_ELEMENT(element))
2013       {
2014         Feld[x][y] = GetElementFromGroupElement(element);
2015
2016         InitField(x, y, init_game);
2017       }
2018
2019       break;
2020   }
2021
2022   if (!init_game)
2023     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2024 }
2025
2026 static void InitField_WithBug1(int x, int y, boolean init_game)
2027 {
2028   InitField(x, y, init_game);
2029
2030   // not needed to call InitMovDir() -- already done by InitField()!
2031   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2032       CAN_MOVE(Feld[x][y]))
2033     InitMovDir(x, y);
2034 }
2035
2036 static void InitField_WithBug2(int x, int y, boolean init_game)
2037 {
2038   int old_element = Feld[x][y];
2039
2040   InitField(x, y, init_game);
2041
2042   // not needed to call InitMovDir() -- already done by InitField()!
2043   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2044       CAN_MOVE(old_element) &&
2045       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2046     InitMovDir(x, y);
2047
2048   /* this case is in fact a combination of not less than three bugs:
2049      first, it calls InitMovDir() for elements that can move, although this is
2050      already done by InitField(); then, it checks the element that was at this
2051      field _before_ the call to InitField() (which can change it); lastly, it
2052      was not called for "mole with direction" elements, which were treated as
2053      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2054   */
2055 }
2056
2057 static int get_key_element_from_nr(int key_nr)
2058 {
2059   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2060                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2061                           EL_EM_KEY_1 : EL_KEY_1);
2062
2063   return key_base_element + key_nr;
2064 }
2065
2066 static int get_next_dropped_element(struct PlayerInfo *player)
2067 {
2068   return (player->inventory_size > 0 ?
2069           player->inventory_element[player->inventory_size - 1] :
2070           player->inventory_infinite_element != EL_UNDEFINED ?
2071           player->inventory_infinite_element :
2072           player->dynabombs_left > 0 ?
2073           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2074           EL_UNDEFINED);
2075 }
2076
2077 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2078 {
2079   // pos >= 0: get element from bottom of the stack;
2080   // pos <  0: get element from top of the stack
2081
2082   if (pos < 0)
2083   {
2084     int min_inventory_size = -pos;
2085     int inventory_pos = player->inventory_size - min_inventory_size;
2086     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2087
2088     return (player->inventory_size >= min_inventory_size ?
2089             player->inventory_element[inventory_pos] :
2090             player->inventory_infinite_element != EL_UNDEFINED ?
2091             player->inventory_infinite_element :
2092             player->dynabombs_left >= min_dynabombs_left ?
2093             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2094             EL_UNDEFINED);
2095   }
2096   else
2097   {
2098     int min_dynabombs_left = pos + 1;
2099     int min_inventory_size = pos + 1 - player->dynabombs_left;
2100     int inventory_pos = pos - player->dynabombs_left;
2101
2102     return (player->inventory_infinite_element != EL_UNDEFINED ?
2103             player->inventory_infinite_element :
2104             player->dynabombs_left >= min_dynabombs_left ?
2105             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2106             player->inventory_size >= min_inventory_size ?
2107             player->inventory_element[inventory_pos] :
2108             EL_UNDEFINED);
2109   }
2110 }
2111
2112 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2113 {
2114   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2115   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2116   int compare_result;
2117
2118   if (gpo1->sort_priority != gpo2->sort_priority)
2119     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2120   else
2121     compare_result = gpo1->nr - gpo2->nr;
2122
2123   return compare_result;
2124 }
2125
2126 int getPlayerInventorySize(int player_nr)
2127 {
2128   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2129     return game_em.ply[player_nr]->dynamite;
2130   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2131     return game_sp.red_disk_count;
2132   else
2133     return stored_player[player_nr].inventory_size;
2134 }
2135
2136 static void InitGameControlValues(void)
2137 {
2138   int i;
2139
2140   for (i = 0; game_panel_controls[i].nr != -1; i++)
2141   {
2142     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2143     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2144     struct TextPosInfo *pos = gpc->pos;
2145     int nr = gpc->nr;
2146     int type = gpc->type;
2147
2148     if (nr != i)
2149     {
2150       Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2151       Error(ERR_EXIT, "this should not happen -- please debug");
2152     }
2153
2154     // force update of game controls after initialization
2155     gpc->value = gpc->last_value = -1;
2156     gpc->frame = gpc->last_frame = -1;
2157     gpc->gfx_frame = -1;
2158
2159     // determine panel value width for later calculation of alignment
2160     if (type == TYPE_INTEGER || type == TYPE_STRING)
2161     {
2162       pos->width = pos->size * getFontWidth(pos->font);
2163       pos->height = getFontHeight(pos->font);
2164     }
2165     else if (type == TYPE_ELEMENT)
2166     {
2167       pos->width = pos->size;
2168       pos->height = pos->size;
2169     }
2170
2171     // fill structure for game panel draw order
2172     gpo->nr = gpc->nr;
2173     gpo->sort_priority = pos->sort_priority;
2174   }
2175
2176   // sort game panel controls according to sort_priority and control number
2177   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2178         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2179 }
2180
2181 static void UpdatePlayfieldElementCount(void)
2182 {
2183   boolean use_element_count = FALSE;
2184   int i, j, x, y;
2185
2186   // first check if it is needed at all to calculate playfield element count
2187   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2188     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2189       use_element_count = TRUE;
2190
2191   if (!use_element_count)
2192     return;
2193
2194   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2195     element_info[i].element_count = 0;
2196
2197   SCAN_PLAYFIELD(x, y)
2198   {
2199     element_info[Feld[x][y]].element_count++;
2200   }
2201
2202   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2203     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2204       if (IS_IN_GROUP(j, i))
2205         element_info[EL_GROUP_START + i].element_count +=
2206           element_info[j].element_count;
2207 }
2208
2209 static void UpdateGameControlValues(void)
2210 {
2211   int i, k;
2212   int time = (game.LevelSolved ?
2213               game.LevelSolved_CountingTime :
2214               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2215               game_em.lev->time :
2216               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2217               game_sp.time_played :
2218               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2219               game_mm.energy_left :
2220               game.no_time_limit ? TimePlayed : TimeLeft);
2221   int score = (game.LevelSolved ?
2222                game.LevelSolved_CountingScore :
2223                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2224                game_em.lev->score :
2225                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2226                game_sp.score :
2227                level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2228                game_mm.score :
2229                game.score);
2230   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2231               game_em.lev->gems_needed :
2232               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2233               game_sp.infotrons_still_needed :
2234               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2235               game_mm.kettles_still_needed :
2236               game.gems_still_needed);
2237   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2238                      game_em.lev->gems_needed > 0 :
2239                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2240                      game_sp.infotrons_still_needed > 0 :
2241                      level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2242                      game_mm.kettles_still_needed > 0 ||
2243                      game_mm.lights_still_needed > 0 :
2244                      game.gems_still_needed > 0 ||
2245                      game.sokoban_fields_still_needed > 0 ||
2246                      game.sokoban_objects_still_needed > 0 ||
2247                      game.lights_still_needed > 0);
2248   int health = (game.LevelSolved ?
2249                 game.LevelSolved_CountingHealth :
2250                 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2251                 MM_HEALTH(game_mm.laser_overload_value) :
2252                 game.health);
2253
2254   UpdatePlayfieldElementCount();
2255
2256   // update game panel control values
2257
2258   // used instead of "level_nr" (for network games)
2259   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = levelset.level_nr;
2260   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2261
2262   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2263   for (i = 0; i < MAX_NUM_KEYS; i++)
2264     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2265   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2266   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2267
2268   if (game.centered_player_nr == -1)
2269   {
2270     for (i = 0; i < MAX_PLAYERS; i++)
2271     {
2272       // only one player in Supaplex game engine
2273       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2274         break;
2275
2276       for (k = 0; k < MAX_NUM_KEYS; k++)
2277       {
2278         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2279         {
2280           if (game_em.ply[i]->keys & (1 << k))
2281             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2282               get_key_element_from_nr(k);
2283         }
2284         else if (stored_player[i].key[k])
2285           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2286             get_key_element_from_nr(k);
2287       }
2288
2289       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2290         getPlayerInventorySize(i);
2291
2292       if (stored_player[i].num_white_keys > 0)
2293         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2294           EL_DC_KEY_WHITE;
2295
2296       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2297         stored_player[i].num_white_keys;
2298     }
2299   }
2300   else
2301   {
2302     int player_nr = game.centered_player_nr;
2303
2304     for (k = 0; k < MAX_NUM_KEYS; k++)
2305     {
2306       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2307       {
2308         if (game_em.ply[player_nr]->keys & (1 << k))
2309           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2310             get_key_element_from_nr(k);
2311       }
2312       else if (stored_player[player_nr].key[k])
2313         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2314           get_key_element_from_nr(k);
2315     }
2316
2317     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2318       getPlayerInventorySize(player_nr);
2319
2320     if (stored_player[player_nr].num_white_keys > 0)
2321       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2322
2323     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2324       stored_player[player_nr].num_white_keys;
2325   }
2326
2327   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2328   {
2329     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2330       get_inventory_element_from_pos(local_player, i);
2331     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2332       get_inventory_element_from_pos(local_player, -i - 1);
2333   }
2334
2335   game_panel_controls[GAME_PANEL_SCORE].value = score;
2336   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2337
2338   game_panel_controls[GAME_PANEL_TIME].value = time;
2339
2340   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2341   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2342   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2343
2344   if (level.time == 0)
2345     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2346   else
2347     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2348
2349   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2350   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2351
2352   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2353
2354   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2355     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2356      EL_EMPTY);
2357   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2358     local_player->shield_normal_time_left;
2359   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2360     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2361      EL_EMPTY);
2362   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2363     local_player->shield_deadly_time_left;
2364
2365   game_panel_controls[GAME_PANEL_EXIT].value =
2366     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2367
2368   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2369     (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2370   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2371     (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2372      EL_EMC_MAGIC_BALL_SWITCH);
2373
2374   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2375     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2376   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2377     game.light_time_left;
2378
2379   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2380     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2381   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2382     game.timegate_time_left;
2383
2384   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2385     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2386
2387   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2388     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2389   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2390     game.lenses_time_left;
2391
2392   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2393     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2394   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2395     game.magnify_time_left;
2396
2397   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2398     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2399      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2400      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2401      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2402      EL_BALLOON_SWITCH_NONE);
2403
2404   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2405     local_player->dynabomb_count;
2406   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2407     local_player->dynabomb_size;
2408   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2409     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2410
2411   game_panel_controls[GAME_PANEL_PENGUINS].value =
2412     game.friends_still_needed;
2413
2414   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2415     game.sokoban_objects_still_needed;
2416   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2417     game.sokoban_fields_still_needed;
2418
2419   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2420     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2421
2422   for (i = 0; i < NUM_BELTS; i++)
2423   {
2424     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2425       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2426        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2427     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2428       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2429   }
2430
2431   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2432     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2433   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2434     game.magic_wall_time_left;
2435
2436   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2437     local_player->gravity;
2438
2439   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2440     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2441
2442   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2443     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2444       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2445        game.panel.element[i].id : EL_UNDEFINED);
2446
2447   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2448     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2449       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2450        element_info[game.panel.element_count[i].id].element_count : 0);
2451
2452   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2453     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2454       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2455        element_info[game.panel.ce_score[i].id].collect_score : 0);
2456
2457   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2458     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2459       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2460        element_info[game.panel.ce_score_element[i].id].collect_score :
2461        EL_UNDEFINED);
2462
2463   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2464   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2465   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2466
2467   // update game panel control frames
2468
2469   for (i = 0; game_panel_controls[i].nr != -1; i++)
2470   {
2471     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2472
2473     if (gpc->type == TYPE_ELEMENT)
2474     {
2475       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2476       {
2477         int last_anim_random_frame = gfx.anim_random_frame;
2478         int element = gpc->value;
2479         int graphic = el2panelimg(element);
2480
2481         if (gpc->value != gpc->last_value)
2482         {
2483           gpc->gfx_frame = 0;
2484           gpc->gfx_random = INIT_GFX_RANDOM();
2485         }
2486         else
2487         {
2488           gpc->gfx_frame++;
2489
2490           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2491               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2492             gpc->gfx_random = INIT_GFX_RANDOM();
2493         }
2494
2495         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2496           gfx.anim_random_frame = gpc->gfx_random;
2497
2498         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2499           gpc->gfx_frame = element_info[element].collect_score;
2500
2501         gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2502                                               gpc->gfx_frame);
2503
2504         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2505           gfx.anim_random_frame = last_anim_random_frame;
2506       }
2507     }
2508     else if (gpc->type == TYPE_GRAPHIC)
2509     {
2510       if (gpc->graphic != IMG_UNDEFINED)
2511       {
2512         int last_anim_random_frame = gfx.anim_random_frame;
2513         int graphic = gpc->graphic;
2514
2515         if (gpc->value != gpc->last_value)
2516         {
2517           gpc->gfx_frame = 0;
2518           gpc->gfx_random = INIT_GFX_RANDOM();
2519         }
2520         else
2521         {
2522           gpc->gfx_frame++;
2523
2524           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2525               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2526             gpc->gfx_random = INIT_GFX_RANDOM();
2527         }
2528
2529         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2530           gfx.anim_random_frame = gpc->gfx_random;
2531
2532         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2533
2534         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2535           gfx.anim_random_frame = last_anim_random_frame;
2536       }
2537     }
2538   }
2539 }
2540
2541 static void DisplayGameControlValues(void)
2542 {
2543   boolean redraw_panel = FALSE;
2544   int i;
2545
2546   for (i = 0; game_panel_controls[i].nr != -1; i++)
2547   {
2548     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2549
2550     if (PANEL_DEACTIVATED(gpc->pos))
2551       continue;
2552
2553     if (gpc->value == gpc->last_value &&
2554         gpc->frame == gpc->last_frame)
2555       continue;
2556
2557     redraw_panel = TRUE;
2558   }
2559
2560   if (!redraw_panel)
2561     return;
2562
2563   // copy default game door content to main double buffer
2564
2565   // !!! CHECK AGAIN !!!
2566   SetPanelBackground();
2567   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2568   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2569
2570   // redraw game control buttons
2571   RedrawGameButtons();
2572
2573   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2574
2575   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2576   {
2577     int nr = game_panel_order[i].nr;
2578     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2579     struct TextPosInfo *pos = gpc->pos;
2580     int type = gpc->type;
2581     int value = gpc->value;
2582     int frame = gpc->frame;
2583     int size = pos->size;
2584     int font = pos->font;
2585     boolean draw_masked = pos->draw_masked;
2586     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2587
2588     if (PANEL_DEACTIVATED(pos))
2589       continue;
2590
2591     gpc->last_value = value;
2592     gpc->last_frame = frame;
2593
2594     if (type == TYPE_INTEGER)
2595     {
2596       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2597           nr == GAME_PANEL_TIME)
2598       {
2599         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2600
2601         if (use_dynamic_size)           // use dynamic number of digits
2602         {
2603           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2604           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2605           int size2 = size1 + 1;
2606           int font1 = pos->font;
2607           int font2 = pos->font_alt;
2608
2609           size = (value < value_change ? size1 : size2);
2610           font = (value < value_change ? font1 : font2);
2611         }
2612       }
2613
2614       // correct text size if "digits" is zero or less
2615       if (size <= 0)
2616         size = strlen(int2str(value, size));
2617
2618       // dynamically correct text alignment
2619       pos->width = size * getFontWidth(font);
2620
2621       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2622                   int2str(value, size), font, mask_mode);
2623     }
2624     else if (type == TYPE_ELEMENT)
2625     {
2626       int element, graphic;
2627       Bitmap *src_bitmap;
2628       int src_x, src_y;
2629       int width, height;
2630       int dst_x = PANEL_XPOS(pos);
2631       int dst_y = PANEL_YPOS(pos);
2632
2633       if (value != EL_UNDEFINED && value != EL_EMPTY)
2634       {
2635         element = value;
2636         graphic = el2panelimg(value);
2637
2638         // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2639
2640         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2641           size = TILESIZE;
2642
2643         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2644                               &src_x, &src_y);
2645
2646         width  = graphic_info[graphic].width  * size / TILESIZE;
2647         height = graphic_info[graphic].height * size / TILESIZE;
2648
2649         if (draw_masked)
2650           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2651                            dst_x, dst_y);
2652         else
2653           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2654                      dst_x, dst_y);
2655       }
2656     }
2657     else if (type == TYPE_GRAPHIC)
2658     {
2659       int graphic        = gpc->graphic;
2660       int graphic_active = gpc->graphic_active;
2661       Bitmap *src_bitmap;
2662       int src_x, src_y;
2663       int width, height;
2664       int dst_x = PANEL_XPOS(pos);
2665       int dst_y = PANEL_YPOS(pos);
2666       boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2667                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2668
2669       if (graphic != IMG_UNDEFINED && !skip)
2670       {
2671         if (pos->style == STYLE_REVERSE)
2672           value = 100 - value;
2673
2674         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2675
2676         if (pos->direction & MV_HORIZONTAL)
2677         {
2678           width  = graphic_info[graphic_active].width * value / 100;
2679           height = graphic_info[graphic_active].height;
2680
2681           if (pos->direction == MV_LEFT)
2682           {
2683             src_x += graphic_info[graphic_active].width - width;
2684             dst_x += graphic_info[graphic_active].width - width;
2685           }
2686         }
2687         else
2688         {
2689           width  = graphic_info[graphic_active].width;
2690           height = graphic_info[graphic_active].height * value / 100;
2691
2692           if (pos->direction == MV_UP)
2693           {
2694             src_y += graphic_info[graphic_active].height - height;
2695             dst_y += graphic_info[graphic_active].height - height;
2696           }
2697         }
2698
2699         if (draw_masked)
2700           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2701                            dst_x, dst_y);
2702         else
2703           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2704                      dst_x, dst_y);
2705
2706         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2707
2708         if (pos->direction & MV_HORIZONTAL)
2709         {
2710           if (pos->direction == MV_RIGHT)
2711           {
2712             src_x += width;
2713             dst_x += width;
2714           }
2715           else
2716           {
2717             dst_x = PANEL_XPOS(pos);
2718           }
2719
2720           width = graphic_info[graphic].width - width;
2721         }
2722         else
2723         {
2724           if (pos->direction == MV_DOWN)
2725           {
2726             src_y += height;
2727             dst_y += height;
2728           }
2729           else
2730           {
2731             dst_y = PANEL_YPOS(pos);
2732           }
2733
2734           height = graphic_info[graphic].height - height;
2735         }
2736
2737         if (draw_masked)
2738           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2739                            dst_x, dst_y);
2740         else
2741           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2742                      dst_x, dst_y);
2743       }
2744     }
2745     else if (type == TYPE_STRING)
2746     {
2747       boolean active = (value != 0);
2748       char *state_normal = "off";
2749       char *state_active = "on";
2750       char *state = (active ? state_active : state_normal);
2751       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2752                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2753                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2754                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2755
2756       if (nr == GAME_PANEL_GRAVITY_STATE)
2757       {
2758         int font1 = pos->font;          // (used for normal state)
2759         int font2 = pos->font_alt;      // (used for active state)
2760
2761         font = (active ? font2 : font1);
2762       }
2763
2764       if (s != NULL)
2765       {
2766         char *s_cut;
2767
2768         if (size <= 0)
2769         {
2770           // don't truncate output if "chars" is zero or less
2771           size = strlen(s);
2772
2773           // dynamically correct text alignment
2774           pos->width = size * getFontWidth(font);
2775         }
2776
2777         s_cut = getStringCopyN(s, size);
2778
2779         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2780                     s_cut, font, mask_mode);
2781
2782         free(s_cut);
2783       }
2784     }
2785
2786     redraw_mask |= REDRAW_DOOR_1;
2787   }
2788
2789   SetGameStatus(GAME_MODE_PLAYING);
2790 }
2791
2792 void UpdateAndDisplayGameControlValues(void)
2793 {
2794   if (tape.deactivate_display)
2795     return;
2796
2797   UpdateGameControlValues();
2798   DisplayGameControlValues();
2799 }
2800
2801 #if 0
2802 static void UpdateGameDoorValues(void)
2803 {
2804   UpdateGameControlValues();
2805 }
2806 #endif
2807
2808 void DrawGameDoorValues(void)
2809 {
2810   DisplayGameControlValues();
2811 }
2812
2813
2814 // ============================================================================
2815 // InitGameEngine()
2816 // ----------------------------------------------------------------------------
2817 // initialize game engine due to level / tape version number
2818 // ============================================================================
2819
2820 static void InitGameEngine(void)
2821 {
2822   int i, j, k, l, x, y;
2823
2824   // set game engine from tape file when re-playing, else from level file
2825   game.engine_version = (tape.playing ? tape.engine_version :
2826                          level.game_version);
2827
2828   // set single or multi-player game mode (needed for re-playing tapes)
2829   game.team_mode = setup.team_mode;
2830
2831   if (tape.playing)
2832   {
2833     int num_players = 0;
2834
2835     for (i = 0; i < MAX_PLAYERS; i++)
2836       if (tape.player_participates[i])
2837         num_players++;
2838
2839     // multi-player tapes contain input data for more than one player
2840     game.team_mode = (num_players > 1);
2841   }
2842
2843   // --------------------------------------------------------------------------
2844   // set flags for bugs and changes according to active game engine version
2845   // --------------------------------------------------------------------------
2846
2847   /*
2848     Summary of bugfix/change:
2849     Fixed handling for custom elements that change when pushed by the player.
2850
2851     Fixed/changed in version:
2852     3.1.0
2853
2854     Description:
2855     Before 3.1.0, custom elements that "change when pushing" changed directly
2856     after the player started pushing them (until then handled in "DigField()").
2857     Since 3.1.0, these custom elements are not changed until the "pushing"
2858     move of the element is finished (now handled in "ContinueMoving()").
2859
2860     Affected levels/tapes:
2861     The first condition is generally needed for all levels/tapes before version
2862     3.1.0, which might use the old behaviour before it was changed; known tapes
2863     that are affected are some tapes from the level set "Walpurgis Gardens" by
2864     Jamie Cullen.
2865     The second condition is an exception from the above case and is needed for
2866     the special case of tapes recorded with game (not engine!) version 3.1.0 or
2867     above (including some development versions of 3.1.0), but before it was
2868     known that this change would break tapes like the above and was fixed in
2869     3.1.1, so that the changed behaviour was active although the engine version
2870     while recording maybe was before 3.1.0. There is at least one tape that is
2871     affected by this exception, which is the tape for the one-level set "Bug
2872     Machine" by Juergen Bonhagen.
2873   */
2874
2875   game.use_change_when_pushing_bug =
2876     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2877      !(tape.playing &&
2878        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2879        tape.game_version <  VERSION_IDENT(3,1,1,0)));
2880
2881   /*
2882     Summary of bugfix/change:
2883     Fixed handling for blocking the field the player leaves when moving.
2884
2885     Fixed/changed in version:
2886     3.1.1
2887
2888     Description:
2889     Before 3.1.1, when "block last field when moving" was enabled, the field
2890     the player is leaving when moving was blocked for the time of the move,
2891     and was directly unblocked afterwards. This resulted in the last field
2892     being blocked for exactly one less than the number of frames of one player
2893     move. Additionally, even when blocking was disabled, the last field was
2894     blocked for exactly one frame.
2895     Since 3.1.1, due to changes in player movement handling, the last field
2896     is not blocked at all when blocking is disabled. When blocking is enabled,
2897     the last field is blocked for exactly the number of frames of one player
2898     move. Additionally, if the player is Murphy, the hero of Supaplex, the
2899     last field is blocked for exactly one more than the number of frames of
2900     one player move.
2901
2902     Affected levels/tapes:
2903     (!!! yet to be determined -- probably many !!!)
2904   */
2905
2906   game.use_block_last_field_bug =
2907     (game.engine_version < VERSION_IDENT(3,1,1,0));
2908
2909   /* various special flags and settings for native Emerald Mine game engine */
2910
2911   game_em.use_single_button =
2912     (game.engine_version > VERSION_IDENT(4,0,0,2));
2913
2914   game_em.use_snap_key_bug =
2915     (game.engine_version < VERSION_IDENT(4,0,1,0));
2916
2917   game_em.use_old_explosions =
2918     (game.engine_version < VERSION_IDENT(4,1,4,2));
2919
2920   // --------------------------------------------------------------------------
2921
2922   // set maximal allowed number of custom element changes per game frame
2923   game.max_num_changes_per_frame = 1;
2924
2925   // default scan direction: scan playfield from top/left to bottom/right
2926   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2927
2928   // dynamically adjust element properties according to game engine version
2929   InitElementPropertiesEngine(game.engine_version);
2930
2931 #if 0
2932   printf("level %d: level version == %06d\n", level_nr, level.game_version);
2933   printf("          tape version == %06d [%s] [file: %06d]\n",
2934          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2935          tape.file_version);
2936   printf("       => game.engine_version == %06d\n", game.engine_version);
2937 #endif
2938
2939   // ---------- initialize player's initial move delay ------------------------
2940
2941   // dynamically adjust player properties according to level information
2942   for (i = 0; i < MAX_PLAYERS; i++)
2943     game.initial_move_delay_value[i] =
2944       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2945
2946   // dynamically adjust player properties according to game engine version
2947   for (i = 0; i < MAX_PLAYERS; i++)
2948     game.initial_move_delay[i] =
2949       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2950        game.initial_move_delay_value[i] : 0);
2951
2952   // ---------- initialize player's initial push delay ------------------------
2953
2954   // dynamically adjust player properties according to game engine version
2955   game.initial_push_delay_value =
2956     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2957
2958   // ---------- initialize changing elements ----------------------------------
2959
2960   // initialize changing elements information
2961   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2962   {
2963     struct ElementInfo *ei = &element_info[i];
2964
2965     // this pointer might have been changed in the level editor
2966     ei->change = &ei->change_page[0];
2967
2968     if (!IS_CUSTOM_ELEMENT(i))
2969     {
2970       ei->change->target_element = EL_EMPTY_SPACE;
2971       ei->change->delay_fixed = 0;
2972       ei->change->delay_random = 0;
2973       ei->change->delay_frames = 1;
2974     }
2975
2976     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2977     {
2978       ei->has_change_event[j] = FALSE;
2979
2980       ei->event_page_nr[j] = 0;
2981       ei->event_page[j] = &ei->change_page[0];
2982     }
2983   }
2984
2985   // add changing elements from pre-defined list
2986   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2987   {
2988     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2989     struct ElementInfo *ei = &element_info[ch_delay->element];
2990
2991     ei->change->target_element       = ch_delay->target_element;
2992     ei->change->delay_fixed          = ch_delay->change_delay;
2993
2994     ei->change->pre_change_function  = ch_delay->pre_change_function;
2995     ei->change->change_function      = ch_delay->change_function;
2996     ei->change->post_change_function = ch_delay->post_change_function;
2997
2998     ei->change->can_change = TRUE;
2999     ei->change->can_change_or_has_action = TRUE;
3000
3001     ei->has_change_event[CE_DELAY] = TRUE;
3002
3003     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3004     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3005   }
3006
3007   // ---------- initialize internal run-time variables ------------------------
3008
3009   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3010   {
3011     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3012
3013     for (j = 0; j < ei->num_change_pages; j++)
3014     {
3015       ei->change_page[j].can_change_or_has_action =
3016         (ei->change_page[j].can_change |
3017          ei->change_page[j].has_action);
3018     }
3019   }
3020
3021   // add change events from custom element configuration
3022   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3023   {
3024     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3025
3026     for (j = 0; j < ei->num_change_pages; j++)
3027     {
3028       if (!ei->change_page[j].can_change_or_has_action)
3029         continue;
3030
3031       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3032       {
3033         // only add event page for the first page found with this event
3034         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3035         {
3036           ei->has_change_event[k] = TRUE;
3037
3038           ei->event_page_nr[k] = j;
3039           ei->event_page[k] = &ei->change_page[j];
3040         }
3041       }
3042     }
3043   }
3044
3045   // ---------- initialize reference elements in change conditions ------------
3046
3047   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3048   {
3049     int element = EL_CUSTOM_START + i;
3050     struct ElementInfo *ei = &element_info[element];
3051
3052     for (j = 0; j < ei->num_change_pages; j++)
3053     {
3054       int trigger_element = ei->change_page[j].initial_trigger_element;
3055
3056       if (trigger_element >= EL_PREV_CE_8 &&
3057           trigger_element <= EL_NEXT_CE_8)
3058         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3059
3060       ei->change_page[j].trigger_element = trigger_element;
3061     }
3062   }
3063
3064   // ---------- initialize run-time trigger player and element ----------------
3065
3066   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3067   {
3068     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3069
3070     for (j = 0; j < ei->num_change_pages; j++)
3071     {
3072       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3073       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3074       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3075       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3076       ei->change_page[j].actual_trigger_ce_value = 0;
3077       ei->change_page[j].actual_trigger_ce_score = 0;
3078     }
3079   }
3080
3081   // ---------- initialize trigger events -------------------------------------
3082
3083   // initialize trigger events information
3084   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3085     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3086       trigger_events[i][j] = FALSE;
3087
3088   // add trigger events from element change event properties
3089   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3090   {
3091     struct ElementInfo *ei = &element_info[i];
3092
3093     for (j = 0; j < ei->num_change_pages; j++)
3094     {
3095       if (!ei->change_page[j].can_change_or_has_action)
3096         continue;
3097
3098       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3099       {
3100         int trigger_element = ei->change_page[j].trigger_element;
3101
3102         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3103         {
3104           if (ei->change_page[j].has_event[k])
3105           {
3106             if (IS_GROUP_ELEMENT(trigger_element))
3107             {
3108               struct ElementGroupInfo *group =
3109                 element_info[trigger_element].group;
3110
3111               for (l = 0; l < group->num_elements_resolved; l++)
3112                 trigger_events[group->element_resolved[l]][k] = TRUE;
3113             }
3114             else if (trigger_element == EL_ANY_ELEMENT)
3115               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3116                 trigger_events[l][k] = TRUE;
3117             else
3118               trigger_events[trigger_element][k] = TRUE;
3119           }
3120         }
3121       }
3122     }
3123   }
3124
3125   // ---------- initialize push delay -----------------------------------------
3126
3127   // initialize push delay values to default
3128   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3129   {
3130     if (!IS_CUSTOM_ELEMENT(i))
3131     {
3132       // set default push delay values (corrected since version 3.0.7-1)
3133       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3134       {
3135         element_info[i].push_delay_fixed = 2;
3136         element_info[i].push_delay_random = 8;
3137       }
3138       else
3139       {
3140         element_info[i].push_delay_fixed = 8;
3141         element_info[i].push_delay_random = 8;
3142       }
3143     }
3144   }
3145
3146   // set push delay value for certain elements from pre-defined list
3147   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3148   {
3149     int e = push_delay_list[i].element;
3150
3151     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3152     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3153   }
3154
3155   // set push delay value for Supaplex elements for newer engine versions
3156   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3157   {
3158     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3159     {
3160       if (IS_SP_ELEMENT(i))
3161       {
3162         // set SP push delay to just enough to push under a falling zonk
3163         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3164
3165         element_info[i].push_delay_fixed  = delay;
3166         element_info[i].push_delay_random = 0;
3167       }
3168     }
3169   }
3170
3171   // ---------- initialize move stepsize --------------------------------------
3172
3173   // initialize move stepsize values to default
3174   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3175     if (!IS_CUSTOM_ELEMENT(i))
3176       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3177
3178   // set move stepsize value for certain elements from pre-defined list
3179   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3180   {
3181     int e = move_stepsize_list[i].element;
3182
3183     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3184   }
3185
3186   // ---------- initialize collect score --------------------------------------
3187
3188   // initialize collect score values for custom elements from initial value
3189   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3190     if (IS_CUSTOM_ELEMENT(i))
3191       element_info[i].collect_score = element_info[i].collect_score_initial;
3192
3193   // ---------- initialize collect count --------------------------------------
3194
3195   // initialize collect count values for non-custom elements
3196   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3197     if (!IS_CUSTOM_ELEMENT(i))
3198       element_info[i].collect_count_initial = 0;
3199
3200   // add collect count values for all elements from pre-defined list
3201   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3202     element_info[collect_count_list[i].element].collect_count_initial =
3203       collect_count_list[i].count;
3204
3205   // ---------- initialize access direction -----------------------------------
3206
3207   // initialize access direction values to default (access from every side)
3208   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3209     if (!IS_CUSTOM_ELEMENT(i))
3210       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3211
3212   // set access direction value for certain elements from pre-defined list
3213   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3214     element_info[access_direction_list[i].element].access_direction =
3215       access_direction_list[i].direction;
3216
3217   // ---------- initialize explosion content ----------------------------------
3218   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3219   {
3220     if (IS_CUSTOM_ELEMENT(i))
3221       continue;
3222
3223     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3224     {
3225       // (content for EL_YAMYAM set at run-time with game.yamyam_content_nr)
3226
3227       element_info[i].content.e[x][y] =
3228         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3229          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3230          i == EL_PLAYER_3 ? EL_EMERALD :
3231          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3232          i == EL_MOLE ? EL_EMERALD_RED :
3233          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3234          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3235          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3236          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3237          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3238          i == EL_WALL_EMERALD ? EL_EMERALD :
3239          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3240          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3241          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3242          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3243          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3244          i == EL_WALL_PEARL ? EL_PEARL :
3245          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3246          EL_EMPTY);
3247     }
3248   }
3249
3250   // ---------- initialize recursion detection --------------------------------
3251   recursion_loop_depth = 0;
3252   recursion_loop_detected = FALSE;
3253   recursion_loop_element = EL_UNDEFINED;
3254
3255   // ---------- initialize graphics engine ------------------------------------
3256   game.scroll_delay_value =
3257     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3258      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3259   game.scroll_delay_value =
3260     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3261
3262   // ---------- initialize game engine snapshots ------------------------------
3263   for (i = 0; i < MAX_PLAYERS; i++)
3264     game.snapshot.last_action[i] = 0;
3265   game.snapshot.changed_action = FALSE;
3266   game.snapshot.collected_item = FALSE;
3267   game.snapshot.mode =
3268     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3269      SNAPSHOT_MODE_EVERY_STEP :
3270      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3271      SNAPSHOT_MODE_EVERY_MOVE :
3272      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3273      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3274   game.snapshot.save_snapshot = FALSE;
3275
3276   // ---------- initialize level time for Supaplex engine ---------------------
3277   // Supaplex levels with time limit currently unsupported -- should be added
3278   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3279     level.time = 0;
3280 }
3281
3282 static int get_num_special_action(int element, int action_first,
3283                                   int action_last)
3284 {
3285   int num_special_action = 0;
3286   int i, j;
3287
3288   for (i = action_first; i <= action_last; i++)
3289   {
3290     boolean found = FALSE;
3291
3292     for (j = 0; j < NUM_DIRECTIONS; j++)
3293       if (el_act_dir2img(element, i, j) !=
3294           el_act_dir2img(element, ACTION_DEFAULT, j))
3295         found = TRUE;
3296
3297     if (found)
3298       num_special_action++;
3299     else
3300       break;
3301   }
3302
3303   return num_special_action;
3304 }
3305
3306
3307 // ============================================================================
3308 // InitGame()
3309 // ----------------------------------------------------------------------------
3310 // initialize and start new game
3311 // ============================================================================
3312
3313 #if DEBUG_INIT_PLAYER
3314 static void DebugPrintPlayerStatus(char *message)
3315 {
3316   int i;
3317
3318   if (!options.debug)
3319     return;
3320
3321   printf("%s:\n", message);
3322
3323   for (i = 0; i < MAX_PLAYERS; i++)
3324   {
3325     struct PlayerInfo *player = &stored_player[i];
3326
3327     printf("- player %d: present == %d, connected == %d [%d/%d], active == %d",
3328            i + 1,
3329            player->present,
3330            player->connected,
3331            player->connected_locally,
3332            player->connected_network,
3333            player->active);
3334
3335     if (local_player == player)
3336       printf(" (local player)");
3337
3338     printf("\n");
3339   }
3340 }
3341 #endif
3342
3343 void InitGame(void)
3344 {
3345   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3346   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3347   int fade_mask = REDRAW_FIELD;
3348
3349   boolean emulate_bd = TRUE;    // unless non-BOULDERDASH elements found
3350   boolean emulate_sb = TRUE;    // unless non-SOKOBAN     elements found
3351   boolean emulate_sp = TRUE;    // unless non-SUPAPLEX    elements found
3352   int initial_move_dir = MV_DOWN;
3353   int i, j, x, y;
3354
3355   // required here to update video display before fading (FIX THIS)
3356   DrawMaskedBorder(REDRAW_DOOR_2);
3357
3358   if (!game.restart_level)
3359     CloseDoor(DOOR_CLOSE_1);
3360
3361   SetGameStatus(GAME_MODE_PLAYING);
3362
3363   if (level_editor_test_game)
3364     FadeSkipNextFadeOut();
3365   else
3366     FadeSetEnterScreen();
3367
3368   if (CheckFadeAll())
3369     fade_mask = REDRAW_ALL;
3370
3371   FadeLevelSoundsAndMusic();
3372
3373   ExpireSoundLoops(TRUE);
3374
3375   FadeOut(fade_mask);
3376
3377   if (level_editor_test_game)
3378     FadeSkipNextFadeIn();
3379
3380   // needed if different viewport properties defined for playing
3381   ChangeViewportPropertiesIfNeeded();
3382
3383   ClearField();
3384
3385   DrawCompleteVideoDisplay();
3386
3387   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3388
3389   InitGameEngine();
3390   InitGameControlValues();
3391
3392   // don't play tapes over network
3393   network_playing = (network.enabled && !tape.playing);
3394
3395   for (i = 0; i < MAX_PLAYERS; i++)
3396   {
3397     struct PlayerInfo *player = &stored_player[i];
3398
3399     player->index_nr = i;
3400     player->index_bit = (1 << i);
3401     player->element_nr = EL_PLAYER_1 + i;
3402
3403     player->present = FALSE;
3404     player->active = FALSE;
3405     player->mapped = FALSE;
3406
3407     player->killed = FALSE;
3408     player->reanimated = FALSE;
3409     player->buried = FALSE;
3410
3411     player->action = 0;
3412     player->effective_action = 0;
3413     player->programmed_action = 0;
3414     player->snap_action = 0;
3415
3416     player->mouse_action.lx = 0;
3417     player->mouse_action.ly = 0;
3418     player->mouse_action.button = 0;
3419     player->mouse_action.button_hint = 0;
3420
3421     player->effective_mouse_action.lx = 0;
3422     player->effective_mouse_action.ly = 0;
3423     player->effective_mouse_action.button = 0;
3424     player->effective_mouse_action.button_hint = 0;
3425
3426     for (j = 0; j < MAX_NUM_KEYS; j++)
3427       player->key[j] = FALSE;
3428
3429     player->num_white_keys = 0;
3430
3431     player->dynabomb_count = 0;
3432     player->dynabomb_size = 1;
3433     player->dynabombs_left = 0;
3434     player->dynabomb_xl = FALSE;
3435
3436     player->MovDir = initial_move_dir;
3437     player->MovPos = 0;
3438     player->GfxPos = 0;
3439     player->GfxDir = initial_move_dir;
3440     player->GfxAction = ACTION_DEFAULT;
3441     player->Frame = 0;
3442     player->StepFrame = 0;
3443
3444     player->initial_element = player->element_nr;
3445     player->artwork_element =
3446       (level.use_artwork_element[i] ? level.artwork_element[i] :
3447        player->element_nr);
3448     player->use_murphy = FALSE;
3449
3450     player->block_last_field = FALSE;   // initialized in InitPlayerField()
3451     player->block_delay_adjustment = 0; // initialized in InitPlayerField()
3452
3453     player->gravity = level.initial_player_gravity[i];
3454
3455     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3456
3457     player->actual_frame_counter = 0;
3458
3459     player->step_counter = 0;
3460
3461     player->last_move_dir = initial_move_dir;
3462
3463     player->is_active = FALSE;
3464
3465     player->is_waiting = FALSE;
3466     player->is_moving = FALSE;
3467     player->is_auto_moving = FALSE;
3468     player->is_digging = FALSE;
3469     player->is_snapping = FALSE;
3470     player->is_collecting = FALSE;
3471     player->is_pushing = FALSE;
3472     player->is_switching = FALSE;
3473     player->is_dropping = FALSE;
3474     player->is_dropping_pressed = FALSE;
3475
3476     player->is_bored = FALSE;
3477     player->is_sleeping = FALSE;
3478
3479     player->was_waiting = TRUE;
3480     player->was_moving = FALSE;
3481     player->was_snapping = FALSE;
3482     player->was_dropping = FALSE;
3483
3484     player->force_dropping = FALSE;
3485
3486     player->frame_counter_bored = -1;
3487     player->frame_counter_sleeping = -1;
3488
3489     player->anim_delay_counter = 0;
3490     player->post_delay_counter = 0;
3491
3492     player->dir_waiting = initial_move_dir;
3493     player->action_waiting = ACTION_DEFAULT;
3494     player->last_action_waiting = ACTION_DEFAULT;
3495     player->special_action_bored = ACTION_DEFAULT;
3496     player->special_action_sleeping = ACTION_DEFAULT;
3497
3498     player->switch_x = -1;
3499     player->switch_y = -1;
3500
3501     player->drop_x = -1;
3502     player->drop_y = -1;
3503
3504     player->show_envelope = 0;
3505
3506     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3507
3508     player->push_delay       = -1;      // initialized when pushing starts
3509     player->push_delay_value = game.initial_push_delay_value;
3510
3511     player->drop_delay = 0;
3512     player->drop_pressed_delay = 0;
3513
3514     player->last_jx = -1;
3515     player->last_jy = -1;
3516     player->jx = -1;
3517     player->jy = -1;
3518
3519     player->shield_normal_time_left = 0;
3520     player->shield_deadly_time_left = 0;
3521
3522     player->inventory_infinite_element = EL_UNDEFINED;
3523     player->inventory_size = 0;
3524
3525     if (level.use_initial_inventory[i])
3526     {
3527       for (j = 0; j < level.initial_inventory_size[i]; j++)
3528       {
3529         int element = level.initial_inventory_content[i][j];
3530         int collect_count = element_info[element].collect_count_initial;
3531         int k;
3532
3533         if (!IS_CUSTOM_ELEMENT(element))
3534           collect_count = 1;
3535
3536         if (collect_count == 0)
3537           player->inventory_infinite_element = element;
3538         else
3539           for (k = 0; k < collect_count; k++)
3540             if (player->inventory_size < MAX_INVENTORY_SIZE)
3541               player->inventory_element[player->inventory_size++] = element;
3542       }
3543     }
3544
3545     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3546     SnapField(player, 0, 0);
3547
3548     map_player_action[i] = i;
3549   }
3550
3551   network_player_action_received = FALSE;
3552
3553   // initial null action
3554   if (network_playing)
3555     SendToServer_MovePlayer(MV_NONE);
3556
3557   FrameCounter = 0;
3558   TimeFrames = 0;
3559   TimePlayed = 0;
3560   TimeLeft = level.time;
3561   TapeTime = 0;
3562
3563   ScreenMovDir = MV_NONE;
3564   ScreenMovPos = 0;
3565   ScreenGfxPos = 0;
3566
3567   ScrollStepSize = 0;   // will be correctly initialized by ScrollScreen()
3568
3569   game.robot_wheel_x = -1;
3570   game.robot_wheel_y = -1;
3571
3572   game.exit_x = -1;
3573   game.exit_y = -1;
3574
3575   game.all_players_gone = FALSE;
3576
3577   game.LevelSolved = FALSE;
3578   game.GameOver = FALSE;
3579
3580   game.GamePlayed = !tape.playing;
3581
3582   game.LevelSolved_GameWon = FALSE;
3583   game.LevelSolved_GameEnd = FALSE;
3584   game.LevelSolved_SaveTape = FALSE;
3585   game.LevelSolved_SaveScore = FALSE;
3586
3587   game.LevelSolved_CountingTime = 0;
3588   game.LevelSolved_CountingScore = 0;
3589   game.LevelSolved_CountingHealth = 0;
3590
3591   game.panel.active = TRUE;
3592
3593   game.no_time_limit = (level.time == 0);
3594
3595   game.yamyam_content_nr = 0;
3596   game.robot_wheel_active = FALSE;
3597   game.magic_wall_active = FALSE;
3598   game.magic_wall_time_left = 0;
3599   game.light_time_left = 0;
3600   game.timegate_time_left = 0;
3601   game.switchgate_pos = 0;
3602   game.wind_direction = level.wind_direction_initial;
3603
3604   game.score = 0;
3605   game.score_final = 0;
3606
3607   game.health = MAX_HEALTH;
3608   game.health_final = MAX_HEALTH;
3609
3610   game.gems_still_needed = level.gems_needed;
3611   game.sokoban_fields_still_needed = 0;
3612   game.sokoban_objects_still_needed = 0;
3613   game.lights_still_needed = 0;
3614   game.players_still_needed = 0;
3615   game.friends_still_needed = 0;
3616
3617   game.lenses_time_left = 0;
3618   game.magnify_time_left = 0;
3619
3620   game.ball_state = level.ball_state_initial;
3621   game.ball_content_nr = 0;
3622
3623   game.explosions_delayed = TRUE;
3624
3625   game.envelope_active = FALSE;
3626
3627   for (i = 0; i < NUM_BELTS; i++)
3628   {
3629     game.belt_dir[i] = MV_NONE;
3630     game.belt_dir_nr[i] = 3;            // not moving, next moving left
3631   }
3632
3633   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3634     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3635
3636 #if DEBUG_INIT_PLAYER
3637   DebugPrintPlayerStatus("Player status at level initialization");
3638 #endif
3639
3640   SCAN_PLAYFIELD(x, y)
3641   {
3642     Feld[x][y] = Last[x][y] = level.field[x][y];
3643     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3644     ChangeDelay[x][y] = 0;
3645     ChangePage[x][y] = -1;
3646     CustomValue[x][y] = 0;              // initialized in InitField()
3647     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3648     AmoebaNr[x][y] = 0;
3649     WasJustMoving[x][y] = 0;
3650     WasJustFalling[x][y] = 0;
3651     CheckCollision[x][y] = 0;
3652     CheckImpact[x][y] = 0;
3653     Stop[x][y] = FALSE;
3654     Pushed[x][y] = FALSE;
3655
3656     ChangeCount[x][y] = 0;
3657     ChangeEvent[x][y] = -1;
3658
3659     ExplodePhase[x][y] = 0;
3660     ExplodeDelay[x][y] = 0;
3661     ExplodeField[x][y] = EX_TYPE_NONE;
3662
3663     RunnerVisit[x][y] = 0;
3664     PlayerVisit[x][y] = 0;
3665
3666     GfxFrame[x][y] = 0;
3667     GfxRandom[x][y] = INIT_GFX_RANDOM();
3668     GfxElement[x][y] = EL_UNDEFINED;
3669     GfxAction[x][y] = ACTION_DEFAULT;
3670     GfxDir[x][y] = MV_NONE;
3671     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3672   }
3673
3674   SCAN_PLAYFIELD(x, y)
3675   {
3676     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3677       emulate_bd = FALSE;
3678     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3679       emulate_sb = FALSE;
3680     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3681       emulate_sp = FALSE;
3682
3683     InitField(x, y, TRUE);
3684
3685     ResetGfxAnimation(x, y);
3686   }
3687
3688   InitBeltMovement();
3689
3690   for (i = 0; i < MAX_PLAYERS; i++)
3691   {
3692     struct PlayerInfo *player = &stored_player[i];
3693
3694     // set number of special actions for bored and sleeping animation
3695     player->num_special_action_bored =
3696       get_num_special_action(player->artwork_element,
3697                              ACTION_BORING_1, ACTION_BORING_LAST);
3698     player->num_special_action_sleeping =
3699       get_num_special_action(player->artwork_element,
3700                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3701   }
3702
3703   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3704                     emulate_sb ? EMU_SOKOBAN :
3705                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3706
3707   // initialize type of slippery elements
3708   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3709   {
3710     if (!IS_CUSTOM_ELEMENT(i))
3711     {
3712       // default: elements slip down either to the left or right randomly
3713       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3714
3715       // SP style elements prefer to slip down on the left side
3716       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3717         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3718
3719       // BD style elements prefer to slip down on the left side
3720       if (game.emulation == EMU_BOULDERDASH)
3721         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3722     }
3723   }
3724
3725   // initialize explosion and ignition delay
3726   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3727   {
3728     if (!IS_CUSTOM_ELEMENT(i))
3729     {
3730       int num_phase = 8;
3731       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3732                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3733                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3734       int last_phase = (num_phase + 1) * delay;
3735       int half_phase = (num_phase / 2) * delay;
3736
3737       element_info[i].explosion_delay = last_phase - 1;
3738       element_info[i].ignition_delay = half_phase;
3739
3740       if (i == EL_BLACK_ORB)
3741         element_info[i].ignition_delay = 1;
3742     }
3743   }
3744
3745   // correct non-moving belts to start moving left
3746   for (i = 0; i < NUM_BELTS; i++)
3747     if (game.belt_dir[i] == MV_NONE)
3748       game.belt_dir_nr[i] = 3;          // not moving, next moving left
3749
3750 #if USE_NEW_PLAYER_ASSIGNMENTS
3751   // use preferred player also in local single-player mode
3752   if (!network.enabled && !game.team_mode)
3753   {
3754     int old_index_nr = local_player->index_nr;
3755     int new_index_nr = setup.network_player_nr;
3756
3757     if (new_index_nr >= 0 && new_index_nr < MAX_PLAYERS)
3758     {
3759       stored_player[old_index_nr].connected_locally = FALSE;
3760       stored_player[new_index_nr].connected_locally = TRUE;
3761     }
3762   }
3763
3764   for (i = 0; i < MAX_PLAYERS; i++)
3765   {
3766     stored_player[i].connected = FALSE;
3767
3768     // in network game mode, the local player might not be the first player
3769     if (stored_player[i].connected_locally)
3770       local_player = &stored_player[i];
3771   }
3772
3773   if (!network.enabled)
3774     local_player->connected = TRUE;
3775
3776   if (tape.playing)
3777   {
3778     for (i = 0; i < MAX_PLAYERS; i++)
3779       stored_player[i].connected = tape.player_participates[i];
3780   }
3781   else if (network.enabled)
3782   {
3783     // add team mode players connected over the network (needed for correct
3784     // assignment of player figures from level to locally playing players)
3785
3786     for (i = 0; i < MAX_PLAYERS; i++)
3787       if (stored_player[i].connected_network)
3788         stored_player[i].connected = TRUE;
3789   }
3790   else if (game.team_mode)
3791   {
3792     // try to guess locally connected team mode players (needed for correct
3793     // assignment of player figures from level to locally playing players)
3794
3795     for (i = 0; i < MAX_PLAYERS; i++)
3796       if (setup.input[i].use_joystick ||
3797           setup.input[i].key.left != KSYM_UNDEFINED)
3798         stored_player[i].connected = TRUE;
3799   }
3800
3801 #if DEBUG_INIT_PLAYER
3802   DebugPrintPlayerStatus("Player status after level initialization");
3803 #endif
3804
3805 #if DEBUG_INIT_PLAYER
3806   if (options.debug)
3807     printf("Reassigning players ...\n");
3808 #endif
3809
3810   // check if any connected player was not found in playfield
3811   for (i = 0; i < MAX_PLAYERS; i++)
3812   {
3813     struct PlayerInfo *player = &stored_player[i];
3814
3815     if (player->connected && !player->present)
3816     {
3817       struct PlayerInfo *field_player = NULL;
3818
3819 #if DEBUG_INIT_PLAYER
3820       if (options.debug)
3821         printf("- looking for field player for player %d ...\n", i + 1);
3822 #endif
3823
3824       // assign first free player found that is present in the playfield
3825
3826       // first try: look for unmapped playfield player that is not connected
3827       for (j = 0; j < MAX_PLAYERS; j++)
3828         if (field_player == NULL &&
3829             stored_player[j].present &&
3830             !stored_player[j].mapped &&
3831             !stored_player[j].connected)
3832           field_player = &stored_player[j];
3833
3834       // second try: look for *any* unmapped playfield player
3835       for (j = 0; j < MAX_PLAYERS; j++)
3836         if (field_player == NULL &&
3837             stored_player[j].present &&
3838             !stored_player[j].mapped)
3839           field_player = &stored_player[j];
3840
3841       if (field_player != NULL)
3842       {
3843         int jx = field_player->jx, jy = field_player->jy;
3844
3845 #if DEBUG_INIT_PLAYER
3846         if (options.debug)
3847           printf("- found player %d\n", field_player->index_nr + 1);
3848 #endif
3849
3850         player->present = FALSE;
3851         player->active = FALSE;
3852
3853         field_player->present = TRUE;
3854         field_player->active = TRUE;
3855
3856         /*
3857         player->initial_element = field_player->initial_element;
3858         player->artwork_element = field_player->artwork_element;
3859
3860         player->block_last_field       = field_player->block_last_field;
3861         player->block_delay_adjustment = field_player->block_delay_adjustment;
3862         */
3863
3864         StorePlayer[jx][jy] = field_player->element_nr;
3865
3866         field_player->jx = field_player->last_jx = jx;
3867         field_player->jy = field_player->last_jy = jy;
3868
3869         if (local_player == player)
3870           local_player = field_player;
3871
3872         map_player_action[field_player->index_nr] = i;
3873
3874         field_player->mapped = TRUE;
3875
3876 #if DEBUG_INIT_PLAYER
3877         if (options.debug)
3878           printf("- map_player_action[%d] == %d\n",
3879                  field_player->index_nr + 1, i + 1);
3880 #endif
3881       }
3882     }
3883
3884     if (player->connected && player->present)
3885       player->mapped = TRUE;
3886   }
3887
3888 #if DEBUG_INIT_PLAYER
3889   DebugPrintPlayerStatus("Player status after player assignment (first stage)");
3890 #endif
3891
3892 #else
3893
3894   // check if any connected player was not found in playfield
3895   for (i = 0; i < MAX_PLAYERS; i++)
3896   {
3897     struct PlayerInfo *player = &stored_player[i];
3898
3899     if (player->connected && !player->present)
3900     {
3901       for (j = 0; j < MAX_PLAYERS; j++)
3902       {
3903         struct PlayerInfo *field_player = &stored_player[j];
3904         int jx = field_player->jx, jy = field_player->jy;
3905
3906         // assign first free player found that is present in the playfield
3907         if (field_player->present && !field_player->connected)
3908         {
3909           player->present = TRUE;
3910           player->active = TRUE;
3911
3912           field_player->present = FALSE;
3913           field_player->active = FALSE;
3914
3915           player->initial_element = field_player->initial_element;
3916           player->artwork_element = field_player->artwork_element;
3917
3918           player->block_last_field       = field_player->block_last_field;
3919           player->block_delay_adjustment = field_player->block_delay_adjustment;
3920
3921           StorePlayer[jx][jy] = player->element_nr;
3922
3923           player->jx = player->last_jx = jx;
3924           player->jy = player->last_jy = jy;
3925
3926           break;
3927         }
3928       }
3929     }
3930   }
3931 #endif
3932
3933 #if 0
3934   printf("::: local_player->present == %d\n", local_player->present);
3935 #endif
3936
3937   // set focus to local player for network games, else to all players
3938   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3939   game.centered_player_nr_next = game.centered_player_nr;
3940   game.set_centered_player = FALSE;
3941   game.set_centered_player_wrap = FALSE;
3942
3943   if (network_playing && tape.recording)
3944   {
3945     // store client dependent player focus when recording network games
3946     tape.centered_player_nr_next = game.centered_player_nr_next;
3947     tape.set_centered_player = TRUE;
3948   }
3949
3950   if (tape.playing)
3951   {
3952     // when playing a tape, eliminate all players who do not participate
3953
3954 #if USE_NEW_PLAYER_ASSIGNMENTS
3955
3956     if (!game.team_mode)
3957     {
3958       for (i = 0; i < MAX_PLAYERS; i++)
3959       {
3960         if (stored_player[i].active &&
3961             !tape.player_participates[map_player_action[i]])
3962         {
3963           struct PlayerInfo *player = &stored_player[i];
3964           int jx = player->jx, jy = player->jy;
3965
3966 #if DEBUG_INIT_PLAYER
3967           if (options.debug)
3968             printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
3969 #endif
3970
3971           player->active = FALSE;
3972           StorePlayer[jx][jy] = 0;
3973           Feld[jx][jy] = EL_EMPTY;
3974         }
3975       }
3976     }
3977
3978 #else
3979
3980     for (i = 0; i < MAX_PLAYERS; i++)
3981     {
3982       if (stored_player[i].active &&
3983           !tape.player_participates[i])
3984       {
3985         struct PlayerInfo *player = &stored_player[i];
3986         int jx = player->jx, jy = player->jy;
3987
3988         player->active = FALSE;
3989         StorePlayer[jx][jy] = 0;
3990         Feld[jx][jy] = EL_EMPTY;
3991       }
3992     }
3993 #endif
3994   }
3995   else if (!network.enabled && !game.team_mode)         // && !tape.playing
3996   {
3997     // when in single player mode, eliminate all but the local player
3998
3999     for (i = 0; i < MAX_PLAYERS; i++)
4000     {
4001       struct PlayerInfo *player = &stored_player[i];
4002
4003       if (player->active && player != local_player)
4004       {
4005         int jx = player->jx, jy = player->jy;
4006
4007         player->active = FALSE;
4008         player->present = FALSE;
4009
4010         StorePlayer[jx][jy] = 0;
4011         Feld[jx][jy] = EL_EMPTY;
4012       }
4013     }
4014   }
4015
4016   for (i = 0; i < MAX_PLAYERS; i++)
4017     if (stored_player[i].active)
4018       game.players_still_needed++;
4019
4020   if (level.solved_by_one_player)
4021     game.players_still_needed = 1;
4022
4023   // when recording the game, store which players take part in the game
4024   if (tape.recording)
4025   {
4026 #if USE_NEW_PLAYER_ASSIGNMENTS
4027     for (i = 0; i < MAX_PLAYERS; i++)
4028       if (stored_player[i].connected)
4029         tape.player_participates[i] = TRUE;
4030 #else
4031     for (i = 0; i < MAX_PLAYERS; i++)
4032       if (stored_player[i].active)
4033         tape.player_participates[i] = TRUE;
4034 #endif
4035   }
4036
4037 #if DEBUG_INIT_PLAYER
4038   DebugPrintPlayerStatus("Player status after player assignment (final stage)");
4039 #endif
4040
4041   if (BorderElement == EL_EMPTY)
4042   {
4043     SBX_Left = 0;
4044     SBX_Right = lev_fieldx - SCR_FIELDX;
4045     SBY_Upper = 0;
4046     SBY_Lower = lev_fieldy - SCR_FIELDY;
4047   }
4048   else
4049   {
4050     SBX_Left = -1;
4051     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4052     SBY_Upper = -1;
4053     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4054   }
4055
4056   if (full_lev_fieldx <= SCR_FIELDX)
4057     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4058   if (full_lev_fieldy <= SCR_FIELDY)
4059     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4060
4061   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4062     SBX_Left--;
4063   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4064     SBY_Upper--;
4065
4066   // if local player not found, look for custom element that might create
4067   // the player (make some assumptions about the right custom element)
4068   if (!local_player->present)
4069   {
4070     int start_x = 0, start_y = 0;
4071     int found_rating = 0;
4072     int found_element = EL_UNDEFINED;
4073     int player_nr = local_player->index_nr;
4074
4075     SCAN_PLAYFIELD(x, y)
4076     {
4077       int element = Feld[x][y];
4078       int content;
4079       int xx, yy;
4080       boolean is_player;
4081
4082       if (level.use_start_element[player_nr] &&
4083           level.start_element[player_nr] == element &&
4084           found_rating < 4)
4085       {
4086         start_x = x;
4087         start_y = y;
4088
4089         found_rating = 4;
4090         found_element = element;
4091       }
4092
4093       if (!IS_CUSTOM_ELEMENT(element))
4094         continue;
4095
4096       if (CAN_CHANGE(element))
4097       {
4098         for (i = 0; i < element_info[element].num_change_pages; i++)
4099         {
4100           // check for player created from custom element as single target
4101           content = element_info[element].change_page[i].target_element;
4102           is_player = ELEM_IS_PLAYER(content);
4103
4104           if (is_player && (found_rating < 3 ||
4105                             (found_rating == 3 && element < found_element)))
4106           {
4107             start_x = x;
4108             start_y = y;
4109
4110             found_rating = 3;
4111             found_element = element;
4112           }
4113         }
4114       }
4115
4116       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4117       {
4118         // check for player created from custom element as explosion content
4119         content = element_info[element].content.e[xx][yy];
4120         is_player = ELEM_IS_PLAYER(content);
4121
4122         if (is_player && (found_rating < 2 ||
4123                           (found_rating == 2 && element < found_element)))
4124         {
4125           start_x = x + xx - 1;
4126           start_y = y + yy - 1;
4127
4128           found_rating = 2;
4129           found_element = element;
4130         }
4131
4132         if (!CAN_CHANGE(element))
4133           continue;
4134
4135         for (i = 0; i < element_info[element].num_change_pages; i++)
4136         {
4137           // check for player created from custom element as extended target
4138           content =
4139             element_info[element].change_page[i].target_content.e[xx][yy];
4140
4141           is_player = ELEM_IS_PLAYER(content);
4142
4143           if (is_player && (found_rating < 1 ||
4144                             (found_rating == 1 && element < found_element)))
4145           {
4146             start_x = x + xx - 1;
4147             start_y = y + yy - 1;
4148
4149             found_rating = 1;
4150             found_element = element;
4151           }
4152         }
4153       }
4154     }
4155
4156     scroll_x = SCROLL_POSITION_X(start_x);
4157     scroll_y = SCROLL_POSITION_Y(start_y);
4158   }
4159   else
4160   {
4161     scroll_x = SCROLL_POSITION_X(local_player->jx);
4162     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4163   }
4164
4165   // !!! FIX THIS (START) !!!
4166   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4167   {
4168     InitGameEngine_EM();
4169   }
4170   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4171   {
4172     InitGameEngine_SP();
4173   }
4174   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4175   {
4176     InitGameEngine_MM();
4177   }
4178   else
4179   {
4180     DrawLevel(REDRAW_FIELD);
4181     DrawAllPlayers();
4182
4183     // after drawing the level, correct some elements
4184     if (game.timegate_time_left == 0)
4185       CloseAllOpenTimegates();
4186   }
4187
4188   // blit playfield from scroll buffer to normal back buffer for fading in
4189   BlitScreenToBitmap(backbuffer);
4190   // !!! FIX THIS (END) !!!
4191
4192   DrawMaskedBorder(fade_mask);
4193
4194   FadeIn(fade_mask);
4195
4196 #if 1
4197   // full screen redraw is required at this point in the following cases:
4198   // - special editor door undrawn when game was started from level editor
4199   // - drawing area (playfield) was changed and has to be removed completely
4200   redraw_mask = REDRAW_ALL;
4201   BackToFront();
4202 #endif
4203
4204   if (!game.restart_level)
4205   {
4206     // copy default game door content to main double buffer
4207
4208     // !!! CHECK AGAIN !!!
4209     SetPanelBackground();
4210     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4211     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4212   }
4213
4214   SetPanelBackground();
4215   SetDrawBackgroundMask(REDRAW_DOOR_1);
4216
4217   UpdateAndDisplayGameControlValues();
4218
4219   if (!game.restart_level)
4220   {
4221     UnmapGameButtons();
4222     UnmapTapeButtons();
4223
4224     FreeGameButtons();
4225     CreateGameButtons();
4226
4227     MapGameButtons();
4228     MapTapeButtons();
4229
4230     // copy actual game door content to door double buffer for OpenDoor()
4231     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4232
4233     OpenDoor(DOOR_OPEN_ALL);
4234
4235     KeyboardAutoRepeatOffUnlessAutoplay();
4236
4237 #if DEBUG_INIT_PLAYER
4238     DebugPrintPlayerStatus("Player status (final)");
4239 #endif
4240   }
4241
4242   UnmapAllGadgets();
4243
4244   MapGameButtons();
4245   MapTapeButtons();
4246
4247   if (!game.restart_level && !tape.playing)
4248   {
4249     LevelStats_incPlayed(level_nr);
4250
4251     SaveLevelSetup_SeriesInfo();
4252   }
4253
4254   game.restart_level = FALSE;
4255   game.restart_game_message = NULL;
4256   game.request_active = FALSE;
4257
4258   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4259     InitGameActions_MM();
4260
4261   SaveEngineSnapshotToListInitial();
4262
4263   if (!game.restart_level)
4264   {
4265     PlaySound(SND_GAME_STARTING);
4266
4267     if (setup.sound_music)
4268       PlayLevelMusic();
4269   }
4270 }
4271
4272 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4273                         int actual_player_x, int actual_player_y)
4274 {
4275   // this is used for non-R'n'D game engines to update certain engine values
4276
4277   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4278   {
4279     actual_player_x = correctLevelPosX_EM(actual_player_x);
4280     actual_player_y = correctLevelPosY_EM(actual_player_y);
4281   }
4282
4283   // needed to determine if sounds are played within the visible screen area
4284   scroll_x = actual_scroll_x;
4285   scroll_y = actual_scroll_y;
4286
4287   // needed to get player position for "follow finger" playing input method
4288   local_player->jx = actual_player_x;
4289   local_player->jy = actual_player_y;
4290 }
4291
4292 void InitMovDir(int x, int y)
4293 {
4294   int i, element = Feld[x][y];
4295   static int xy[4][2] =
4296   {
4297     {  0, +1 },
4298     { +1,  0 },
4299     {  0, -1 },
4300     { -1,  0 }
4301   };
4302   static int direction[3][4] =
4303   {
4304     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4305     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4306     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4307   };
4308
4309   switch (element)
4310   {
4311     case EL_BUG_RIGHT:
4312     case EL_BUG_UP:
4313     case EL_BUG_LEFT:
4314     case EL_BUG_DOWN:
4315       Feld[x][y] = EL_BUG;
4316       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4317       break;
4318
4319     case EL_SPACESHIP_RIGHT:
4320     case EL_SPACESHIP_UP:
4321     case EL_SPACESHIP_LEFT:
4322     case EL_SPACESHIP_DOWN:
4323       Feld[x][y] = EL_SPACESHIP;
4324       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4325       break;
4326
4327     case EL_BD_BUTTERFLY_RIGHT:
4328     case EL_BD_BUTTERFLY_UP:
4329     case EL_BD_BUTTERFLY_LEFT:
4330     case EL_BD_BUTTERFLY_DOWN:
4331       Feld[x][y] = EL_BD_BUTTERFLY;
4332       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4333       break;
4334
4335     case EL_BD_FIREFLY_RIGHT:
4336     case EL_BD_FIREFLY_UP:
4337     case EL_BD_FIREFLY_LEFT:
4338     case EL_BD_FIREFLY_DOWN:
4339       Feld[x][y] = EL_BD_FIREFLY;
4340       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4341       break;
4342
4343     case EL_PACMAN_RIGHT:
4344     case EL_PACMAN_UP:
4345     case EL_PACMAN_LEFT:
4346     case EL_PACMAN_DOWN:
4347       Feld[x][y] = EL_PACMAN;
4348       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4349       break;
4350
4351     case EL_YAMYAM_LEFT:
4352     case EL_YAMYAM_RIGHT:
4353     case EL_YAMYAM_UP:
4354     case EL_YAMYAM_DOWN:
4355       Feld[x][y] = EL_YAMYAM;
4356       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4357       break;
4358
4359     case EL_SP_SNIKSNAK:
4360       MovDir[x][y] = MV_UP;
4361       break;
4362
4363     case EL_SP_ELECTRON:
4364       MovDir[x][y] = MV_LEFT;
4365       break;
4366
4367     case EL_MOLE_LEFT:
4368     case EL_MOLE_RIGHT:
4369     case EL_MOLE_UP:
4370     case EL_MOLE_DOWN:
4371       Feld[x][y] = EL_MOLE;
4372       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4373       break;
4374
4375     default:
4376       if (IS_CUSTOM_ELEMENT(element))
4377       {
4378         struct ElementInfo *ei = &element_info[element];
4379         int move_direction_initial = ei->move_direction_initial;
4380         int move_pattern = ei->move_pattern;
4381
4382         if (move_direction_initial == MV_START_PREVIOUS)
4383         {
4384           if (MovDir[x][y] != MV_NONE)
4385             return;
4386
4387           move_direction_initial = MV_START_AUTOMATIC;
4388         }
4389
4390         if (move_direction_initial == MV_START_RANDOM)
4391           MovDir[x][y] = 1 << RND(4);
4392         else if (move_direction_initial & MV_ANY_DIRECTION)
4393           MovDir[x][y] = move_direction_initial;
4394         else if (move_pattern == MV_ALL_DIRECTIONS ||
4395                  move_pattern == MV_TURNING_LEFT ||
4396                  move_pattern == MV_TURNING_RIGHT ||
4397                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4398                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4399                  move_pattern == MV_TURNING_RANDOM)
4400           MovDir[x][y] = 1 << RND(4);
4401         else if (move_pattern == MV_HORIZONTAL)
4402           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4403         else if (move_pattern == MV_VERTICAL)
4404           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4405         else if (move_pattern & MV_ANY_DIRECTION)
4406           MovDir[x][y] = element_info[element].move_pattern;
4407         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4408                  move_pattern == MV_ALONG_RIGHT_SIDE)
4409         {
4410           // use random direction as default start direction
4411           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4412             MovDir[x][y] = 1 << RND(4);
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 (move_pattern == MV_ALONG_RIGHT_SIDE)
4422                 MovDir[x][y] = direction[0][i];
4423               else
4424                 MovDir[x][y] = direction[1][i];
4425
4426               break;
4427             }
4428           }
4429         }                
4430       }
4431       else
4432       {
4433         MovDir[x][y] = 1 << RND(4);
4434
4435         if (element != EL_BUG &&
4436             element != EL_SPACESHIP &&
4437             element != EL_BD_BUTTERFLY &&
4438             element != EL_BD_FIREFLY)
4439           break;
4440
4441         for (i = 0; i < NUM_DIRECTIONS; i++)
4442         {
4443           int x1 = x + xy[i][0];
4444           int y1 = y + xy[i][1];
4445
4446           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4447           {
4448             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4449             {
4450               MovDir[x][y] = direction[0][i];
4451               break;
4452             }
4453             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4454                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4455             {
4456               MovDir[x][y] = direction[1][i];
4457               break;
4458             }
4459           }
4460         }
4461       }
4462       break;
4463   }
4464
4465   GfxDir[x][y] = MovDir[x][y];
4466 }
4467
4468 void InitAmoebaNr(int x, int y)
4469 {
4470   int i;
4471   int group_nr = AmoebeNachbarNr(x, y);
4472
4473   if (group_nr == 0)
4474   {
4475     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4476     {
4477       if (AmoebaCnt[i] == 0)
4478       {
4479         group_nr = i;
4480         break;
4481       }
4482     }
4483   }
4484
4485   AmoebaNr[x][y] = group_nr;
4486   AmoebaCnt[group_nr]++;
4487   AmoebaCnt2[group_nr]++;
4488 }
4489
4490 static void LevelSolved(void)
4491 {
4492   if (level.game_engine_type == GAME_ENGINE_TYPE_RND &&
4493       game.players_still_needed > 0)
4494     return;
4495
4496   game.LevelSolved = TRUE;
4497   game.GameOver = TRUE;
4498
4499   game.score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4500                       game_em.lev->score :
4501                       level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4502                       game_mm.score :
4503                       game.score);
4504   game.health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4505                        MM_HEALTH(game_mm.laser_overload_value) :
4506                        game.health);
4507
4508   game.LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed : TimeLeft);
4509   game.LevelSolved_CountingScore = game.score_final;
4510   game.LevelSolved_CountingHealth = game.health_final;
4511 }
4512
4513 void GameWon(void)
4514 {
4515   static int time_count_steps;
4516   static int time, time_final;
4517   static int score, score_final;
4518   static int health, health_final;
4519   static int game_over_delay_1 = 0;
4520   static int game_over_delay_2 = 0;
4521   static int game_over_delay_3 = 0;
4522   int game_over_delay_value_1 = 50;
4523   int game_over_delay_value_2 = 25;
4524   int game_over_delay_value_3 = 50;
4525
4526   if (!game.LevelSolved_GameWon)
4527   {
4528     int i;
4529
4530     // do not start end game actions before the player stops moving (to exit)
4531     if (local_player->active && local_player->MovPos)
4532       return;
4533
4534     game.LevelSolved_GameWon = TRUE;
4535     game.LevelSolved_SaveTape = tape.recording;
4536     game.LevelSolved_SaveScore = !tape.playing;
4537
4538     if (!tape.playing)
4539     {
4540       LevelStats_incSolved(level_nr);
4541
4542       SaveLevelSetup_SeriesInfo();
4543     }
4544
4545     if (tape.auto_play)         // tape might already be stopped here
4546       tape.auto_play_level_solved = TRUE;
4547
4548     TapeStop();
4549
4550     game_over_delay_1 = 0;
4551     game_over_delay_2 = 0;
4552     game_over_delay_3 = game_over_delay_value_3;
4553
4554     time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4555     score = score_final = game.score_final;
4556     health = health_final = game.health_final;
4557
4558     if (level.score[SC_TIME_BONUS] > 0)
4559     {
4560       if (TimeLeft > 0)
4561       {
4562         time_final = 0;
4563         score_final += TimeLeft * level.score[SC_TIME_BONUS];
4564       }
4565       else if (game.no_time_limit && TimePlayed < 999)
4566       {
4567         time_final = 999;
4568         score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4569       }
4570
4571       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4572
4573       game_over_delay_1 = game_over_delay_value_1;
4574
4575       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4576       {
4577         health_final = 0;
4578         score_final += health * level.score[SC_TIME_BONUS];
4579
4580         game_over_delay_2 = game_over_delay_value_2;
4581       }
4582
4583       game.score_final = score_final;
4584       game.health_final = health_final;
4585     }
4586
4587     if (level_editor_test_game)
4588     {
4589       time = time_final;
4590       score = score_final;
4591
4592       game.LevelSolved_CountingTime = time;
4593       game.LevelSolved_CountingScore = score;
4594
4595       game_panel_controls[GAME_PANEL_TIME].value = time;
4596       game_panel_controls[GAME_PANEL_SCORE].value = score;
4597
4598       DisplayGameControlValues();
4599     }
4600
4601     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4602     {
4603       // check if last player has left the level
4604       if (game.exit_x >= 0 &&
4605           game.exit_y >= 0)
4606       {
4607         int x = game.exit_x;
4608         int y = game.exit_y;
4609         int element = Feld[x][y];
4610
4611         // close exit door after last player
4612         if ((game.all_players_gone &&
4613              (element == EL_EXIT_OPEN ||
4614               element == EL_SP_EXIT_OPEN ||
4615               element == EL_STEEL_EXIT_OPEN)) ||
4616             element == EL_EM_EXIT_OPEN ||
4617             element == EL_EM_STEEL_EXIT_OPEN)
4618         {
4619
4620           Feld[x][y] =
4621             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4622              element == EL_EM_EXIT_OPEN         ? EL_EM_EXIT_CLOSING :
4623              element == EL_SP_EXIT_OPEN         ? EL_SP_EXIT_CLOSING:
4624              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4625              EL_EM_STEEL_EXIT_CLOSING);
4626
4627           PlayLevelSoundElementAction(x, y, element, ACTION_CLOSING);
4628         }
4629
4630         // player disappears
4631         DrawLevelField(x, y);
4632       }
4633
4634       for (i = 0; i < MAX_PLAYERS; i++)
4635       {
4636         struct PlayerInfo *player = &stored_player[i];
4637
4638         if (player->present)
4639         {
4640           RemovePlayer(player);
4641
4642           // player disappears
4643           DrawLevelField(player->jx, player->jy);
4644         }
4645       }
4646     }
4647
4648     PlaySound(SND_GAME_WINNING);
4649   }
4650
4651   if (game_over_delay_1 > 0)
4652   {
4653     game_over_delay_1--;
4654
4655     return;
4656   }
4657
4658   if (time != time_final)
4659   {
4660     int time_to_go = ABS(time_final - time);
4661     int time_count_dir = (time < time_final ? +1 : -1);
4662
4663     if (time_to_go < time_count_steps)
4664       time_count_steps = 1;
4665
4666     time  += time_count_steps * time_count_dir;
4667     score += time_count_steps * level.score[SC_TIME_BONUS];
4668
4669     game.LevelSolved_CountingTime = time;
4670     game.LevelSolved_CountingScore = score;
4671
4672     game_panel_controls[GAME_PANEL_TIME].value = time;
4673     game_panel_controls[GAME_PANEL_SCORE].value = score;
4674
4675     DisplayGameControlValues();
4676
4677     if (time == time_final)
4678       StopSound(SND_GAME_LEVELTIME_BONUS);
4679     else if (setup.sound_loops)
4680       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4681     else
4682       PlaySound(SND_GAME_LEVELTIME_BONUS);
4683
4684     return;
4685   }
4686
4687   if (game_over_delay_2 > 0)
4688   {
4689     game_over_delay_2--;
4690
4691     return;
4692   }
4693
4694   if (health != health_final)
4695   {
4696     int health_count_dir = (health < health_final ? +1 : -1);
4697
4698     health += health_count_dir;
4699     score  += level.score[SC_TIME_BONUS];
4700
4701     game.LevelSolved_CountingHealth = health;
4702     game.LevelSolved_CountingScore = score;
4703
4704     game_panel_controls[GAME_PANEL_HEALTH].value = health;
4705     game_panel_controls[GAME_PANEL_SCORE].value = score;
4706
4707     DisplayGameControlValues();
4708
4709     if (health == health_final)
4710       StopSound(SND_GAME_LEVELTIME_BONUS);
4711     else if (setup.sound_loops)
4712       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4713     else
4714       PlaySound(SND_GAME_LEVELTIME_BONUS);
4715
4716     return;
4717   }
4718
4719   game.panel.active = FALSE;
4720
4721   if (game_over_delay_3 > 0)
4722   {
4723     game_over_delay_3--;
4724
4725     return;
4726   }
4727
4728   GameEnd();
4729 }
4730
4731 void GameEnd(void)
4732 {
4733   // used instead of "level_nr" (needed for network games)
4734   int last_level_nr = levelset.level_nr;
4735   int hi_pos;
4736
4737   game.LevelSolved_GameEnd = TRUE;
4738
4739   if (game.LevelSolved_SaveTape)
4740   {
4741     // make sure that request dialog to save tape does not open door again
4742     if (!global.use_envelope_request)
4743       CloseDoor(DOOR_CLOSE_1);
4744
4745     SaveTapeChecked_LevelSolved(tape.level_nr);         // ask to save tape
4746   }
4747
4748   // if no tape is to be saved, close both doors simultaneously
4749   CloseDoor(DOOR_CLOSE_ALL);
4750
4751   if (level_editor_test_game)
4752   {
4753     SetGameStatus(GAME_MODE_MAIN);
4754
4755     DrawMainMenu();
4756
4757     return;
4758   }
4759
4760   if (!game.LevelSolved_SaveScore)
4761   {
4762     SetGameStatus(GAME_MODE_MAIN);
4763
4764     DrawMainMenu();
4765
4766     return;
4767   }
4768
4769   if (level_nr == leveldir_current->handicap_level)
4770   {
4771     leveldir_current->handicap_level++;
4772
4773     SaveLevelSetup_SeriesInfo();
4774   }
4775
4776   if (setup.increment_levels &&
4777       level_nr < leveldir_current->last_level &&
4778       !network_playing)
4779   {
4780     level_nr++;         // advance to next level
4781     TapeErase();        // start with empty tape
4782
4783     if (setup.auto_play_next_level)
4784     {
4785       LoadLevel(level_nr);
4786
4787       SaveLevelSetup_SeriesInfo();
4788     }
4789   }
4790
4791   hi_pos = NewHiScore(last_level_nr);
4792
4793   if (hi_pos >= 0 && !setup.skip_scores_after_game)
4794   {
4795     SetGameStatus(GAME_MODE_SCORES);
4796
4797     DrawHallOfFame(last_level_nr, hi_pos);
4798   }
4799   else if (setup.auto_play_next_level && setup.increment_levels &&
4800            last_level_nr < leveldir_current->last_level &&
4801            !network_playing)
4802   {
4803     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
4804   }
4805   else
4806   {
4807     SetGameStatus(GAME_MODE_MAIN);
4808
4809     DrawMainMenu();
4810   }
4811 }
4812
4813 int NewHiScore(int level_nr)
4814 {
4815   int k, l;
4816   int position = -1;
4817   boolean one_score_entry_per_name = !program.many_scores_per_name;
4818
4819   LoadScore(level_nr);
4820
4821   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4822       game.score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
4823     return -1;
4824
4825   for (k = 0; k < MAX_SCORE_ENTRIES; k++)
4826   {
4827     if (game.score_final > highscore[k].Score)
4828     {
4829       // player has made it to the hall of fame
4830
4831       if (k < MAX_SCORE_ENTRIES - 1)
4832       {
4833         int m = MAX_SCORE_ENTRIES - 1;
4834
4835         if (one_score_entry_per_name)
4836         {
4837           for (l = k; l < MAX_SCORE_ENTRIES; l++)
4838             if (strEqual(setup.player_name, highscore[l].Name))
4839               m = l;
4840
4841           if (m == k)   // player's new highscore overwrites his old one
4842             goto put_into_list;
4843         }
4844
4845         for (l = m; l > k; l--)
4846         {
4847           strcpy(highscore[l].Name, highscore[l - 1].Name);
4848           highscore[l].Score = highscore[l - 1].Score;
4849         }
4850       }
4851
4852       put_into_list:
4853
4854       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4855       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4856       highscore[k].Score = game.score_final;
4857       position = k;
4858
4859       break;
4860     }
4861     else if (one_score_entry_per_name &&
4862              !strncmp(setup.player_name, highscore[k].Name,
4863                       MAX_PLAYER_NAME_LEN))
4864       break;    // player already there with a higher score
4865   }
4866
4867   if (position >= 0) 
4868     SaveScore(level_nr);
4869
4870   return position;
4871 }
4872
4873 static int getElementMoveStepsizeExt(int x, int y, int direction)
4874 {
4875   int element = Feld[x][y];
4876   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4877   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4878   int horiz_move = (dx != 0);
4879   int sign = (horiz_move ? dx : dy);
4880   int step = sign * element_info[element].move_stepsize;
4881
4882   // special values for move stepsize for spring and things on conveyor belt
4883   if (horiz_move)
4884   {
4885     if (CAN_FALL(element) &&
4886         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4887       step = sign * MOVE_STEPSIZE_NORMAL / 2;
4888     else if (element == EL_SPRING)
4889       step = sign * MOVE_STEPSIZE_NORMAL * 2;
4890   }
4891
4892   return step;
4893 }
4894
4895 static int getElementMoveStepsize(int x, int y)
4896 {
4897   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4898 }
4899
4900 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4901 {
4902   if (player->GfxAction != action || player->GfxDir != dir)
4903   {
4904     player->GfxAction = action;
4905     player->GfxDir = dir;
4906     player->Frame = 0;
4907     player->StepFrame = 0;
4908   }
4909 }
4910
4911 static void ResetGfxFrame(int x, int y)
4912 {
4913   // profiling showed that "autotest" spends 10~20% of its time in this function
4914   if (DrawingDeactivatedField())
4915     return;
4916
4917   int element = Feld[x][y];
4918   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4919
4920   if (graphic_info[graphic].anim_global_sync)
4921     GfxFrame[x][y] = FrameCounter;
4922   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4923     GfxFrame[x][y] = CustomValue[x][y];
4924   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4925     GfxFrame[x][y] = element_info[element].collect_score;
4926   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4927     GfxFrame[x][y] = ChangeDelay[x][y];
4928 }
4929
4930 static void ResetGfxAnimation(int x, int y)
4931 {
4932   GfxAction[x][y] = ACTION_DEFAULT;
4933   GfxDir[x][y] = MovDir[x][y];
4934   GfxFrame[x][y] = 0;
4935
4936   ResetGfxFrame(x, y);
4937 }
4938
4939 static void ResetRandomAnimationValue(int x, int y)
4940 {
4941   GfxRandom[x][y] = INIT_GFX_RANDOM();
4942 }
4943
4944 static void InitMovingField(int x, int y, int direction)
4945 {
4946   int element = Feld[x][y];
4947   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4948   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4949   int newx = x + dx;
4950   int newy = y + dy;
4951   boolean is_moving_before, is_moving_after;
4952
4953   // check if element was/is moving or being moved before/after mode change
4954   is_moving_before = (WasJustMoving[x][y] != 0);
4955   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
4956
4957   // reset animation only for moving elements which change direction of moving
4958   // or which just started or stopped moving
4959   // (else CEs with property "can move" / "not moving" are reset each frame)
4960   if (is_moving_before != is_moving_after ||
4961       direction != MovDir[x][y])
4962     ResetGfxAnimation(x, y);
4963
4964   MovDir[x][y] = direction;
4965   GfxDir[x][y] = direction;
4966
4967   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4968                      direction == MV_DOWN && CAN_FALL(element) ?
4969                      ACTION_FALLING : ACTION_MOVING);
4970
4971   // this is needed for CEs with property "can move" / "not moving"
4972
4973   if (is_moving_after)
4974   {
4975     if (Feld[newx][newy] == EL_EMPTY)
4976       Feld[newx][newy] = EL_BLOCKED;
4977
4978     MovDir[newx][newy] = MovDir[x][y];
4979
4980     CustomValue[newx][newy] = CustomValue[x][y];
4981
4982     GfxFrame[newx][newy] = GfxFrame[x][y];
4983     GfxRandom[newx][newy] = GfxRandom[x][y];
4984     GfxAction[newx][newy] = GfxAction[x][y];
4985     GfxDir[newx][newy] = GfxDir[x][y];
4986   }
4987 }
4988
4989 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4990 {
4991   int direction = MovDir[x][y];
4992   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4993   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
4994
4995   *goes_to_x = newx;
4996   *goes_to_y = newy;
4997 }
4998
4999 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5000 {
5001   int oldx = x, oldy = y;
5002   int direction = MovDir[x][y];
5003
5004   if (direction == MV_LEFT)
5005     oldx++;
5006   else if (direction == MV_RIGHT)
5007     oldx--;
5008   else if (direction == MV_UP)
5009     oldy++;
5010   else if (direction == MV_DOWN)
5011     oldy--;
5012
5013   *comes_from_x = oldx;
5014   *comes_from_y = oldy;
5015 }
5016
5017 static int MovingOrBlocked2Element(int x, int y)
5018 {
5019   int element = Feld[x][y];
5020
5021   if (element == EL_BLOCKED)
5022   {
5023     int oldx, oldy;
5024
5025     Blocked2Moving(x, y, &oldx, &oldy);
5026     return Feld[oldx][oldy];
5027   }
5028   else
5029     return element;
5030 }
5031
5032 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5033 {
5034   // like MovingOrBlocked2Element(), but if element is moving
5035   // and (x,y) is the field the moving element is just leaving,
5036   // return EL_BLOCKED instead of the element value
5037   int element = Feld[x][y];
5038
5039   if (IS_MOVING(x, y))
5040   {
5041     if (element == EL_BLOCKED)
5042     {
5043       int oldx, oldy;
5044
5045       Blocked2Moving(x, y, &oldx, &oldy);
5046       return Feld[oldx][oldy];
5047     }
5048     else
5049       return EL_BLOCKED;
5050   }
5051   else
5052     return element;
5053 }
5054
5055 static void RemoveField(int x, int y)
5056 {
5057   Feld[x][y] = EL_EMPTY;
5058
5059   MovPos[x][y] = 0;
5060   MovDir[x][y] = 0;
5061   MovDelay[x][y] = 0;
5062
5063   CustomValue[x][y] = 0;
5064
5065   AmoebaNr[x][y] = 0;
5066   ChangeDelay[x][y] = 0;
5067   ChangePage[x][y] = -1;
5068   Pushed[x][y] = FALSE;
5069
5070   GfxElement[x][y] = EL_UNDEFINED;
5071   GfxAction[x][y] = ACTION_DEFAULT;
5072   GfxDir[x][y] = MV_NONE;
5073 }
5074
5075 static void RemoveMovingField(int x, int y)
5076 {
5077   int oldx = x, oldy = y, newx = x, newy = y;
5078   int element = Feld[x][y];
5079   int next_element = EL_UNDEFINED;
5080
5081   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5082     return;
5083
5084   if (IS_MOVING(x, y))
5085   {
5086     Moving2Blocked(x, y, &newx, &newy);
5087
5088     if (Feld[newx][newy] != EL_BLOCKED)
5089     {
5090       // element is moving, but target field is not free (blocked), but
5091       // already occupied by something different (example: acid pool);
5092       // in this case, only remove the moving field, but not the target
5093
5094       RemoveField(oldx, oldy);
5095
5096       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5097
5098       TEST_DrawLevelField(oldx, oldy);
5099
5100       return;
5101     }
5102   }
5103   else if (element == EL_BLOCKED)
5104   {
5105     Blocked2Moving(x, y, &oldx, &oldy);
5106     if (!IS_MOVING(oldx, oldy))
5107       return;
5108   }
5109
5110   if (element == EL_BLOCKED &&
5111       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5112        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5113        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5114        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5115        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5116        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5117     next_element = get_next_element(Feld[oldx][oldy]);
5118
5119   RemoveField(oldx, oldy);
5120   RemoveField(newx, newy);
5121
5122   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5123
5124   if (next_element != EL_UNDEFINED)
5125     Feld[oldx][oldy] = next_element;
5126
5127   TEST_DrawLevelField(oldx, oldy);
5128   TEST_DrawLevelField(newx, newy);
5129 }
5130
5131 void DrawDynamite(int x, int y)
5132 {
5133   int sx = SCREENX(x), sy = SCREENY(y);
5134   int graphic = el2img(Feld[x][y]);
5135   int frame;
5136
5137   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5138     return;
5139
5140   if (IS_WALKABLE_INSIDE(Back[x][y]))
5141     return;
5142
5143   if (Back[x][y])
5144     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5145   else if (Store[x][y])
5146     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5147
5148   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5149
5150   if (Back[x][y] || Store[x][y])
5151     DrawGraphicThruMask(sx, sy, graphic, frame);
5152   else
5153     DrawGraphic(sx, sy, graphic, frame);
5154 }
5155
5156 static void CheckDynamite(int x, int y)
5157 {
5158   if (MovDelay[x][y] != 0)      // dynamite is still waiting to explode
5159   {
5160     MovDelay[x][y]--;
5161
5162     if (MovDelay[x][y] != 0)
5163     {
5164       DrawDynamite(x, y);
5165       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5166
5167       return;
5168     }
5169   }
5170
5171   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5172
5173   Bang(x, y);
5174 }
5175
5176 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5177 {
5178   boolean num_checked_players = 0;
5179   int i;
5180
5181   for (i = 0; i < MAX_PLAYERS; i++)
5182   {
5183     if (stored_player[i].active)
5184     {
5185       int sx = stored_player[i].jx;
5186       int sy = stored_player[i].jy;
5187
5188       if (num_checked_players == 0)
5189       {
5190         *sx1 = *sx2 = sx;
5191         *sy1 = *sy2 = sy;
5192       }
5193       else
5194       {
5195         *sx1 = MIN(*sx1, sx);
5196         *sy1 = MIN(*sy1, sy);
5197         *sx2 = MAX(*sx2, sx);
5198         *sy2 = MAX(*sy2, sy);
5199       }
5200
5201       num_checked_players++;
5202     }
5203   }
5204 }
5205
5206 static boolean checkIfAllPlayersFitToScreen_RND(void)
5207 {
5208   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5209
5210   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5211
5212   return (sx2 - sx1 < SCR_FIELDX &&
5213           sy2 - sy1 < SCR_FIELDY);
5214 }
5215
5216 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5217 {
5218   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5219
5220   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5221
5222   *sx = (sx1 + sx2) / 2;
5223   *sy = (sy1 + sy2) / 2;
5224 }
5225
5226 static void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5227                                boolean center_screen, boolean quick_relocation)
5228 {
5229   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5230   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5231   boolean no_delay = (tape.warp_forward);
5232   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5233   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5234   int new_scroll_x, new_scroll_y;
5235
5236   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5237   {
5238     // case 1: quick relocation inside visible screen (without scrolling)
5239
5240     RedrawPlayfield();
5241
5242     return;
5243   }
5244
5245   if (!level.shifted_relocation || center_screen)
5246   {
5247     // relocation _with_ centering of screen
5248
5249     new_scroll_x = SCROLL_POSITION_X(x);
5250     new_scroll_y = SCROLL_POSITION_Y(y);
5251   }
5252   else
5253   {
5254     // relocation _without_ centering of screen
5255
5256     int center_scroll_x = SCROLL_POSITION_X(old_x);
5257     int center_scroll_y = SCROLL_POSITION_Y(old_y);
5258     int offset_x = x + (scroll_x - center_scroll_x);
5259     int offset_y = y + (scroll_y - center_scroll_y);
5260
5261     // for new screen position, apply previous offset to center position
5262     new_scroll_x = SCROLL_POSITION_X(offset_x);
5263     new_scroll_y = SCROLL_POSITION_Y(offset_y);
5264   }
5265
5266   if (quick_relocation)
5267   {
5268     // case 2: quick relocation (redraw without visible scrolling)
5269
5270     scroll_x = new_scroll_x;
5271     scroll_y = new_scroll_y;
5272
5273     RedrawPlayfield();
5274
5275     return;
5276   }
5277
5278   // case 3: visible relocation (with scrolling to new position)
5279
5280   ScrollScreen(NULL, SCROLL_GO_ON);     // scroll last frame to full tile
5281
5282   SetVideoFrameDelay(wait_delay_value);
5283
5284   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5285   {
5286     int dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5287     int dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5288
5289     if (dx == 0 && dy == 0)             // no scrolling needed at all
5290       break;
5291
5292     scroll_x -= dx;
5293     scroll_y -= dy;
5294
5295     // set values for horizontal/vertical screen scrolling (half tile size)
5296     int dir_x = (dx != 0 ? MV_HORIZONTAL : 0);
5297     int dir_y = (dy != 0 ? MV_VERTICAL   : 0);
5298     int pos_x = dx * TILEX / 2;
5299     int pos_y = dy * TILEY / 2;
5300     int fx = getFieldbufferOffsetX_RND(dir_x, pos_x);
5301     int fy = getFieldbufferOffsetY_RND(dir_y, pos_y);
5302
5303     ScrollLevel(dx, dy);
5304     DrawAllPlayers();
5305
5306     // scroll in two steps of half tile size to make things smoother
5307     BlitScreenToBitmapExt_RND(window, fx, fy);
5308
5309     // scroll second step to align at full tile size
5310     BlitScreenToBitmap(window);
5311   }
5312
5313   DrawAllPlayers();
5314   BackToFront();
5315
5316   SetVideoFrameDelay(frame_delay_value_old);
5317 }
5318
5319 static void RelocatePlayer(int jx, int jy, int el_player_raw)
5320 {
5321   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5322   int player_nr = GET_PLAYER_NR(el_player);
5323   struct PlayerInfo *player = &stored_player[player_nr];
5324   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5325   boolean no_delay = (tape.warp_forward);
5326   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5327   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5328   int old_jx = player->jx;
5329   int old_jy = player->jy;
5330   int old_element = Feld[old_jx][old_jy];
5331   int element = Feld[jx][jy];
5332   boolean player_relocated = (old_jx != jx || old_jy != jy);
5333
5334   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5335   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5336   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5337   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5338   int leave_side_horiz = move_dir_horiz;
5339   int leave_side_vert  = move_dir_vert;
5340   int enter_side = enter_side_horiz | enter_side_vert;
5341   int leave_side = leave_side_horiz | leave_side_vert;
5342
5343   if (player->buried)           // do not reanimate dead player
5344     return;
5345
5346   if (!player_relocated)        // no need to relocate the player
5347     return;
5348
5349   if (IS_PLAYER(jx, jy))        // player already placed at new position
5350   {
5351     RemoveField(jx, jy);        // temporarily remove newly placed player
5352     DrawLevelField(jx, jy);
5353   }
5354
5355   if (player->present)
5356   {
5357     while (player->MovPos)
5358     {
5359       ScrollPlayer(player, SCROLL_GO_ON);
5360       ScrollScreen(NULL, SCROLL_GO_ON);
5361
5362       AdvanceFrameAndPlayerCounters(player->index_nr);
5363
5364       DrawPlayer(player);
5365
5366       BackToFront_WithFrameDelay(wait_delay_value);
5367     }
5368
5369     DrawPlayer(player);         // needed here only to cleanup last field
5370     DrawLevelField(player->jx, player->jy);     // remove player graphic
5371
5372     player->is_moving = FALSE;
5373   }
5374
5375   if (IS_CUSTOM_ELEMENT(old_element))
5376     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5377                                CE_LEFT_BY_PLAYER,
5378                                player->index_bit, leave_side);
5379
5380   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5381                                       CE_PLAYER_LEAVES_X,
5382                                       player->index_bit, leave_side);
5383
5384   Feld[jx][jy] = el_player;
5385   InitPlayerField(jx, jy, el_player, TRUE);
5386
5387   /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5388      possible that the relocation target field did not contain a player element,
5389      but a walkable element, to which the new player was relocated -- in this
5390      case, restore that (already initialized!) element on the player field */
5391   if (!ELEM_IS_PLAYER(element)) // player may be set on walkable element
5392   {
5393     Feld[jx][jy] = element;     // restore previously existing element
5394   }
5395
5396   // only visually relocate centered player
5397   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5398                      FALSE, level.instant_relocation);
5399
5400   TestIfPlayerTouchesBadThing(jx, jy);
5401   TestIfPlayerTouchesCustomElement(jx, jy);
5402
5403   if (IS_CUSTOM_ELEMENT(element))
5404     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5405                                player->index_bit, enter_side);
5406
5407   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5408                                       player->index_bit, enter_side);
5409
5410   if (player->is_switching)
5411   {
5412     /* ensure that relocation while still switching an element does not cause
5413        a new element to be treated as also switched directly after relocation
5414        (this is important for teleporter switches that teleport the player to
5415        a place where another teleporter switch is in the same direction, which
5416        would then incorrectly be treated as immediately switched before the
5417        direction key that caused the switch was released) */
5418
5419     player->switch_x += jx - old_jx;
5420     player->switch_y += jy - old_jy;
5421   }
5422 }
5423
5424 static void Explode(int ex, int ey, int phase, int mode)
5425 {
5426   int x, y;
5427   int last_phase;
5428   int border_element;
5429
5430   // !!! eliminate this variable !!!
5431   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5432
5433   if (game.explosions_delayed)
5434   {
5435     ExplodeField[ex][ey] = mode;
5436     return;
5437   }
5438
5439   if (phase == EX_PHASE_START)          // initialize 'Store[][]' field
5440   {
5441     int center_element = Feld[ex][ey];
5442     int artwork_element, explosion_element;     // set these values later
5443
5444     // remove things displayed in background while burning dynamite
5445     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5446       Back[ex][ey] = 0;
5447
5448     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5449     {
5450       // put moving element to center field (and let it explode there)
5451       center_element = MovingOrBlocked2Element(ex, ey);
5452       RemoveMovingField(ex, ey);
5453       Feld[ex][ey] = center_element;
5454     }
5455
5456     // now "center_element" is finally determined -- set related values now
5457     artwork_element = center_element;           // for custom player artwork
5458     explosion_element = center_element;         // for custom player artwork
5459
5460     if (IS_PLAYER(ex, ey))
5461     {
5462       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5463
5464       artwork_element = stored_player[player_nr].artwork_element;
5465
5466       if (level.use_explosion_element[player_nr])
5467       {
5468         explosion_element = level.explosion_element[player_nr];
5469         artwork_element = explosion_element;
5470       }
5471     }
5472
5473     if (mode == EX_TYPE_NORMAL ||
5474         mode == EX_TYPE_CENTER ||
5475         mode == EX_TYPE_CROSS)
5476       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5477
5478     last_phase = element_info[explosion_element].explosion_delay + 1;
5479
5480     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5481     {
5482       int xx = x - ex + 1;
5483       int yy = y - ey + 1;
5484       int element;
5485
5486       if (!IN_LEV_FIELD(x, y) ||
5487           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5488           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5489         continue;
5490
5491       element = Feld[x][y];
5492
5493       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5494       {
5495         element = MovingOrBlocked2Element(x, y);
5496
5497         if (!IS_EXPLOSION_PROOF(element))
5498           RemoveMovingField(x, y);
5499       }
5500
5501       // indestructible elements can only explode in center (but not flames)
5502       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5503                                            mode == EX_TYPE_BORDER)) ||
5504           element == EL_FLAMES)
5505         continue;
5506
5507       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5508          behaviour, for example when touching a yamyam that explodes to rocks
5509          with active deadly shield, a rock is created under the player !!! */
5510       // (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8)
5511 #if 0
5512       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5513           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5514            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5515 #else
5516       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5517 #endif
5518       {
5519         if (IS_ACTIVE_BOMB(element))
5520         {
5521           // re-activate things under the bomb like gate or penguin
5522           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5523           Back[x][y] = 0;
5524         }
5525
5526         continue;
5527       }
5528
5529       // save walkable background elements while explosion on same tile
5530       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5531           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5532         Back[x][y] = element;
5533
5534       // ignite explodable elements reached by other explosion
5535       if (element == EL_EXPLOSION)
5536         element = Store2[x][y];
5537
5538       if (AmoebaNr[x][y] &&
5539           (element == EL_AMOEBA_FULL ||
5540            element == EL_BD_AMOEBA ||
5541            element == EL_AMOEBA_GROWING))
5542       {
5543         AmoebaCnt[AmoebaNr[x][y]]--;
5544         AmoebaCnt2[AmoebaNr[x][y]]--;
5545       }
5546
5547       RemoveField(x, y);
5548
5549       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5550       {
5551         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5552
5553         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5554
5555         if (PLAYERINFO(ex, ey)->use_murphy)
5556           Store[x][y] = EL_EMPTY;
5557       }
5558
5559       // !!! check this case -- currently needed for rnd_rado_negundo_v,
5560       // !!! levels 015 018 019 020 021 022 023 026 027 028 !!!
5561       else if (ELEM_IS_PLAYER(center_element))
5562         Store[x][y] = EL_EMPTY;
5563       else if (center_element == EL_YAMYAM)
5564         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5565       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5566         Store[x][y] = element_info[center_element].content.e[xx][yy];
5567 #if 1
5568       // needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5569       // (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5570       // otherwise) -- FIX THIS !!!
5571       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5572         Store[x][y] = element_info[element].content.e[1][1];
5573 #else
5574       else if (!CAN_EXPLODE(element))
5575         Store[x][y] = element_info[element].content.e[1][1];
5576 #endif
5577       else
5578         Store[x][y] = EL_EMPTY;
5579
5580       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5581           center_element == EL_AMOEBA_TO_DIAMOND)
5582         Store2[x][y] = element;
5583
5584       Feld[x][y] = EL_EXPLOSION;
5585       GfxElement[x][y] = artwork_element;
5586
5587       ExplodePhase[x][y] = 1;
5588       ExplodeDelay[x][y] = last_phase;
5589
5590       Stop[x][y] = TRUE;
5591     }
5592
5593     if (center_element == EL_YAMYAM)
5594       game.yamyam_content_nr =
5595         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5596
5597     return;
5598   }
5599
5600   if (Stop[ex][ey])
5601     return;
5602
5603   x = ex;
5604   y = ey;
5605
5606   if (phase == 1)
5607     GfxFrame[x][y] = 0;         // restart explosion animation
5608
5609   last_phase = ExplodeDelay[x][y];
5610
5611   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5612
5613   // this can happen if the player leaves an explosion just in time
5614   if (GfxElement[x][y] == EL_UNDEFINED)
5615     GfxElement[x][y] = EL_EMPTY;
5616
5617   border_element = Store2[x][y];
5618   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5619     border_element = StorePlayer[x][y];
5620
5621   if (phase == element_info[border_element].ignition_delay ||
5622       phase == last_phase)
5623   {
5624     boolean border_explosion = FALSE;
5625
5626     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5627         !PLAYER_EXPLOSION_PROTECTED(x, y))
5628     {
5629       KillPlayerUnlessExplosionProtected(x, y);
5630       border_explosion = TRUE;
5631     }
5632     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5633     {
5634       Feld[x][y] = Store2[x][y];
5635       Store2[x][y] = 0;
5636       Bang(x, y);
5637       border_explosion = TRUE;
5638     }
5639     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5640     {
5641       AmoebeUmwandeln(x, y);
5642       Store2[x][y] = 0;
5643       border_explosion = TRUE;
5644     }
5645
5646     // if an element just explodes due to another explosion (chain-reaction),
5647     // do not immediately end the new explosion when it was the last frame of
5648     // the explosion (as it would be done in the following "if"-statement!)
5649     if (border_explosion && phase == last_phase)
5650       return;
5651   }
5652
5653   if (phase == last_phase)
5654   {
5655     int element;
5656
5657     element = Feld[x][y] = Store[x][y];
5658     Store[x][y] = Store2[x][y] = 0;
5659     GfxElement[x][y] = EL_UNDEFINED;
5660
5661     // player can escape from explosions and might therefore be still alive
5662     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5663         element <= EL_PLAYER_IS_EXPLODING_4)
5664     {
5665       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5666       int explosion_element = EL_PLAYER_1 + player_nr;
5667       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5668       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5669
5670       if (level.use_explosion_element[player_nr])
5671         explosion_element = level.explosion_element[player_nr];
5672
5673       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5674                     element_info[explosion_element].content.e[xx][yy]);
5675     }
5676
5677     // restore probably existing indestructible background element
5678     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5679       element = Feld[x][y] = Back[x][y];
5680     Back[x][y] = 0;
5681
5682     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5683     GfxDir[x][y] = MV_NONE;
5684     ChangeDelay[x][y] = 0;
5685     ChangePage[x][y] = -1;
5686
5687     CustomValue[x][y] = 0;
5688
5689     InitField_WithBug2(x, y, FALSE);
5690
5691     TEST_DrawLevelField(x, y);
5692
5693     TestIfElementTouchesCustomElement(x, y);
5694
5695     if (GFX_CRUMBLED(element))
5696       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5697
5698     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5699       StorePlayer[x][y] = 0;
5700
5701     if (ELEM_IS_PLAYER(element))
5702       RelocatePlayer(x, y, element);
5703   }
5704   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5705   {
5706     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5707     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5708
5709     if (phase == delay)
5710       TEST_DrawLevelFieldCrumbled(x, y);
5711
5712     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5713     {
5714       DrawLevelElement(x, y, Back[x][y]);
5715       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5716     }
5717     else if (IS_WALKABLE_UNDER(Back[x][y]))
5718     {
5719       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5720       DrawLevelElementThruMask(x, y, Back[x][y]);
5721     }
5722     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5723       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5724   }
5725 }
5726
5727 static void DynaExplode(int ex, int ey)
5728 {
5729   int i, j;
5730   int dynabomb_element = Feld[ex][ey];
5731   int dynabomb_size = 1;
5732   boolean dynabomb_xl = FALSE;
5733   struct PlayerInfo *player;
5734   static int xy[4][2] =
5735   {
5736     { 0, -1 },
5737     { -1, 0 },
5738     { +1, 0 },
5739     { 0, +1 }
5740   };
5741
5742   if (IS_ACTIVE_BOMB(dynabomb_element))
5743   {
5744     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5745     dynabomb_size = player->dynabomb_size;
5746     dynabomb_xl = player->dynabomb_xl;
5747     player->dynabombs_left++;
5748   }
5749
5750   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5751
5752   for (i = 0; i < NUM_DIRECTIONS; i++)
5753   {
5754     for (j = 1; j <= dynabomb_size; j++)
5755     {
5756       int x = ex + j * xy[i][0];
5757       int y = ey + j * xy[i][1];
5758       int element;
5759
5760       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5761         break;
5762
5763       element = Feld[x][y];
5764
5765       // do not restart explosions of fields with active bombs
5766       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5767         continue;
5768
5769       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5770
5771       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5772           !IS_DIGGABLE(element) && !dynabomb_xl)
5773         break;
5774     }
5775   }
5776 }
5777
5778 void Bang(int x, int y)
5779 {
5780   int element = MovingOrBlocked2Element(x, y);
5781   int explosion_type = EX_TYPE_NORMAL;
5782
5783   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5784   {
5785     struct PlayerInfo *player = PLAYERINFO(x, y);
5786
5787     element = Feld[x][y] = player->initial_element;
5788
5789     if (level.use_explosion_element[player->index_nr])
5790     {
5791       int explosion_element = level.explosion_element[player->index_nr];
5792
5793       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5794         explosion_type = EX_TYPE_CROSS;
5795       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5796         explosion_type = EX_TYPE_CENTER;
5797     }
5798   }
5799
5800   switch (element)
5801   {
5802     case EL_BUG:
5803     case EL_SPACESHIP:
5804     case EL_BD_BUTTERFLY:
5805     case EL_BD_FIREFLY:
5806     case EL_YAMYAM:
5807     case EL_DARK_YAMYAM:
5808     case EL_ROBOT:
5809     case EL_PACMAN:
5810     case EL_MOLE:
5811       RaiseScoreElement(element);
5812       break;
5813
5814     case EL_DYNABOMB_PLAYER_1_ACTIVE:
5815     case EL_DYNABOMB_PLAYER_2_ACTIVE:
5816     case EL_DYNABOMB_PLAYER_3_ACTIVE:
5817     case EL_DYNABOMB_PLAYER_4_ACTIVE:
5818     case EL_DYNABOMB_INCREASE_NUMBER:
5819     case EL_DYNABOMB_INCREASE_SIZE:
5820     case EL_DYNABOMB_INCREASE_POWER:
5821       explosion_type = EX_TYPE_DYNA;
5822       break;
5823
5824     case EL_DC_LANDMINE:
5825       explosion_type = EX_TYPE_CENTER;
5826       break;
5827
5828     case EL_PENGUIN:
5829     case EL_LAMP:
5830     case EL_LAMP_ACTIVE:
5831     case EL_AMOEBA_TO_DIAMOND:
5832       if (!IS_PLAYER(x, y))     // penguin and player may be at same field
5833         explosion_type = EX_TYPE_CENTER;
5834       break;
5835
5836     default:
5837       if (element_info[element].explosion_type == EXPLODES_CROSS)
5838         explosion_type = EX_TYPE_CROSS;
5839       else if (element_info[element].explosion_type == EXPLODES_1X1)
5840         explosion_type = EX_TYPE_CENTER;
5841       break;
5842   }
5843
5844   if (explosion_type == EX_TYPE_DYNA)
5845     DynaExplode(x, y);
5846   else
5847     Explode(x, y, EX_PHASE_START, explosion_type);
5848
5849   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5850 }
5851
5852 static void SplashAcid(int x, int y)
5853 {
5854   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5855       (!IN_LEV_FIELD(x - 1, y - 2) ||
5856        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5857     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5858
5859   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5860       (!IN_LEV_FIELD(x + 1, y - 2) ||
5861        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5862     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5863
5864   PlayLevelSound(x, y, SND_ACID_SPLASHING);
5865 }
5866
5867 static void InitBeltMovement(void)
5868 {
5869   static int belt_base_element[4] =
5870   {
5871     EL_CONVEYOR_BELT_1_LEFT,
5872     EL_CONVEYOR_BELT_2_LEFT,
5873     EL_CONVEYOR_BELT_3_LEFT,
5874     EL_CONVEYOR_BELT_4_LEFT
5875   };
5876   static int belt_base_active_element[4] =
5877   {
5878     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5879     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5880     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5881     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5882   };
5883
5884   int x, y, i, j;
5885
5886   // set frame order for belt animation graphic according to belt direction
5887   for (i = 0; i < NUM_BELTS; i++)
5888   {
5889     int belt_nr = i;
5890
5891     for (j = 0; j < NUM_BELT_PARTS; j++)
5892     {
5893       int element = belt_base_active_element[belt_nr] + j;
5894       int graphic_1 = el2img(element);
5895       int graphic_2 = el2panelimg(element);
5896
5897       if (game.belt_dir[i] == MV_LEFT)
5898       {
5899         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5900         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5901       }
5902       else
5903       {
5904         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5905         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5906       }
5907     }
5908   }
5909
5910   SCAN_PLAYFIELD(x, y)
5911   {
5912     int element = Feld[x][y];
5913
5914     for (i = 0; i < NUM_BELTS; i++)
5915     {
5916       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5917       {
5918         int e_belt_nr = getBeltNrFromBeltElement(element);
5919         int belt_nr = i;
5920
5921         if (e_belt_nr == belt_nr)
5922         {
5923           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5924
5925           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5926         }
5927       }
5928     }
5929   }
5930 }
5931
5932 static void ToggleBeltSwitch(int x, int y)
5933 {
5934   static int belt_base_element[4] =
5935   {
5936     EL_CONVEYOR_BELT_1_LEFT,
5937     EL_CONVEYOR_BELT_2_LEFT,
5938     EL_CONVEYOR_BELT_3_LEFT,
5939     EL_CONVEYOR_BELT_4_LEFT
5940   };
5941   static int belt_base_active_element[4] =
5942   {
5943     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5944     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5945     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5946     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5947   };
5948   static int belt_base_switch_element[4] =
5949   {
5950     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5951     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5952     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5953     EL_CONVEYOR_BELT_4_SWITCH_LEFT
5954   };
5955   static int belt_move_dir[4] =
5956   {
5957     MV_LEFT,
5958     MV_NONE,
5959     MV_RIGHT,
5960     MV_NONE,
5961   };
5962
5963   int element = Feld[x][y];
5964   int belt_nr = getBeltNrFromBeltSwitchElement(element);
5965   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5966   int belt_dir = belt_move_dir[belt_dir_nr];
5967   int xx, yy, i;
5968
5969   if (!IS_BELT_SWITCH(element))
5970     return;
5971
5972   game.belt_dir_nr[belt_nr] = belt_dir_nr;
5973   game.belt_dir[belt_nr] = belt_dir;
5974
5975   if (belt_dir_nr == 3)
5976     belt_dir_nr = 1;
5977
5978   // set frame order for belt animation graphic according to belt direction
5979   for (i = 0; i < NUM_BELT_PARTS; i++)
5980   {
5981     int element = belt_base_active_element[belt_nr] + i;
5982     int graphic_1 = el2img(element);
5983     int graphic_2 = el2panelimg(element);
5984
5985     if (belt_dir == MV_LEFT)
5986     {
5987       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5988       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5989     }
5990     else
5991     {
5992       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5993       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5994     }
5995   }
5996
5997   SCAN_PLAYFIELD(xx, yy)
5998   {
5999     int element = Feld[xx][yy];
6000
6001     if (IS_BELT_SWITCH(element))
6002     {
6003       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6004
6005       if (e_belt_nr == belt_nr)
6006       {
6007         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6008         TEST_DrawLevelField(xx, yy);
6009       }
6010     }
6011     else if (IS_BELT(element) && belt_dir != MV_NONE)
6012     {
6013       int e_belt_nr = getBeltNrFromBeltElement(element);
6014
6015       if (e_belt_nr == belt_nr)
6016       {
6017         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
6018
6019         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6020         TEST_DrawLevelField(xx, yy);
6021       }
6022     }
6023     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6024     {
6025       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6026
6027       if (e_belt_nr == belt_nr)
6028       {
6029         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
6030
6031         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
6032         TEST_DrawLevelField(xx, yy);
6033       }
6034     }
6035   }
6036 }
6037
6038 static void ToggleSwitchgateSwitch(int x, int y)
6039 {
6040   int xx, yy;
6041
6042   game.switchgate_pos = !game.switchgate_pos;
6043
6044   SCAN_PLAYFIELD(xx, yy)
6045   {
6046     int element = Feld[xx][yy];
6047
6048     if (element == EL_SWITCHGATE_SWITCH_UP)
6049     {
6050       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6051       TEST_DrawLevelField(xx, yy);
6052     }
6053     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6054     {
6055       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6056       TEST_DrawLevelField(xx, yy);
6057     }
6058     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6059     {
6060       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6061       TEST_DrawLevelField(xx, yy);
6062     }
6063     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6064     {
6065       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6066       TEST_DrawLevelField(xx, yy);
6067     }
6068     else if (element == EL_SWITCHGATE_OPEN ||
6069              element == EL_SWITCHGATE_OPENING)
6070     {
6071       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6072
6073       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6074     }
6075     else if (element == EL_SWITCHGATE_CLOSED ||
6076              element == EL_SWITCHGATE_CLOSING)
6077     {
6078       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6079
6080       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6081     }
6082   }
6083 }
6084
6085 static int getInvisibleActiveFromInvisibleElement(int element)
6086 {
6087   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6088           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6089           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6090           element);
6091 }
6092
6093 static int getInvisibleFromInvisibleActiveElement(int element)
6094 {
6095   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6096           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6097           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6098           element);
6099 }
6100
6101 static void RedrawAllLightSwitchesAndInvisibleElements(void)
6102 {
6103   int x, y;
6104
6105   SCAN_PLAYFIELD(x, y)
6106   {
6107     int element = Feld[x][y];
6108
6109     if (element == EL_LIGHT_SWITCH &&
6110         game.light_time_left > 0)
6111     {
6112       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6113       TEST_DrawLevelField(x, y);
6114     }
6115     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6116              game.light_time_left == 0)
6117     {
6118       Feld[x][y] = EL_LIGHT_SWITCH;
6119       TEST_DrawLevelField(x, y);
6120     }
6121     else if (element == EL_EMC_DRIPPER &&
6122              game.light_time_left > 0)
6123     {
6124       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6125       TEST_DrawLevelField(x, y);
6126     }
6127     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6128              game.light_time_left == 0)
6129     {
6130       Feld[x][y] = EL_EMC_DRIPPER;
6131       TEST_DrawLevelField(x, y);
6132     }
6133     else if (element == EL_INVISIBLE_STEELWALL ||
6134              element == EL_INVISIBLE_WALL ||
6135              element == EL_INVISIBLE_SAND)
6136     {
6137       if (game.light_time_left > 0)
6138         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6139
6140       TEST_DrawLevelField(x, y);
6141
6142       // uncrumble neighbour fields, if needed
6143       if (element == EL_INVISIBLE_SAND)
6144         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6145     }
6146     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6147              element == EL_INVISIBLE_WALL_ACTIVE ||
6148              element == EL_INVISIBLE_SAND_ACTIVE)
6149     {
6150       if (game.light_time_left == 0)
6151         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6152
6153       TEST_DrawLevelField(x, y);
6154
6155       // re-crumble neighbour fields, if needed
6156       if (element == EL_INVISIBLE_SAND)
6157         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6158     }
6159   }
6160 }
6161
6162 static void RedrawAllInvisibleElementsForLenses(void)
6163 {
6164   int x, y;
6165
6166   SCAN_PLAYFIELD(x, y)
6167   {
6168     int element = Feld[x][y];
6169
6170     if (element == EL_EMC_DRIPPER &&
6171         game.lenses_time_left > 0)
6172     {
6173       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6174       TEST_DrawLevelField(x, y);
6175     }
6176     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6177              game.lenses_time_left == 0)
6178     {
6179       Feld[x][y] = EL_EMC_DRIPPER;
6180       TEST_DrawLevelField(x, y);
6181     }
6182     else if (element == EL_INVISIBLE_STEELWALL ||
6183              element == EL_INVISIBLE_WALL ||
6184              element == EL_INVISIBLE_SAND)
6185     {
6186       if (game.lenses_time_left > 0)
6187         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6188
6189       TEST_DrawLevelField(x, y);
6190
6191       // uncrumble neighbour fields, if needed
6192       if (element == EL_INVISIBLE_SAND)
6193         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6194     }
6195     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6196              element == EL_INVISIBLE_WALL_ACTIVE ||
6197              element == EL_INVISIBLE_SAND_ACTIVE)
6198     {
6199       if (game.lenses_time_left == 0)
6200         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6201
6202       TEST_DrawLevelField(x, y);
6203
6204       // re-crumble neighbour fields, if needed
6205       if (element == EL_INVISIBLE_SAND)
6206         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6207     }
6208   }
6209 }
6210
6211 static void RedrawAllInvisibleElementsForMagnifier(void)
6212 {
6213   int x, y;
6214
6215   SCAN_PLAYFIELD(x, y)
6216   {
6217     int element = Feld[x][y];
6218
6219     if (element == EL_EMC_FAKE_GRASS &&
6220         game.magnify_time_left > 0)
6221     {
6222       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6223       TEST_DrawLevelField(x, y);
6224     }
6225     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6226              game.magnify_time_left == 0)
6227     {
6228       Feld[x][y] = EL_EMC_FAKE_GRASS;
6229       TEST_DrawLevelField(x, y);
6230     }
6231     else if (IS_GATE_GRAY(element) &&
6232              game.magnify_time_left > 0)
6233     {
6234       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6235                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6236                     IS_EM_GATE_GRAY(element) ?
6237                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6238                     IS_EMC_GATE_GRAY(element) ?
6239                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6240                     IS_DC_GATE_GRAY(element) ?
6241                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6242                     element);
6243       TEST_DrawLevelField(x, y);
6244     }
6245     else if (IS_GATE_GRAY_ACTIVE(element) &&
6246              game.magnify_time_left == 0)
6247     {
6248       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6249                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6250                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6251                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6252                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6253                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6254                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6255                     EL_DC_GATE_WHITE_GRAY :
6256                     element);
6257       TEST_DrawLevelField(x, y);
6258     }
6259   }
6260 }
6261
6262 static void ToggleLightSwitch(int x, int y)
6263 {
6264   int element = Feld[x][y];
6265
6266   game.light_time_left =
6267     (element == EL_LIGHT_SWITCH ?
6268      level.time_light * FRAMES_PER_SECOND : 0);
6269
6270   RedrawAllLightSwitchesAndInvisibleElements();
6271 }
6272
6273 static void ActivateTimegateSwitch(int x, int y)
6274 {
6275   int xx, yy;
6276
6277   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6278
6279   SCAN_PLAYFIELD(xx, yy)
6280   {
6281     int element = Feld[xx][yy];
6282
6283     if (element == EL_TIMEGATE_CLOSED ||
6284         element == EL_TIMEGATE_CLOSING)
6285     {
6286       Feld[xx][yy] = EL_TIMEGATE_OPENING;
6287       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6288     }
6289
6290     /*
6291     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6292     {
6293       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6294       TEST_DrawLevelField(xx, yy);
6295     }
6296     */
6297
6298   }
6299
6300   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6301                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6302 }
6303
6304 static void Impact(int x, int y)
6305 {
6306   boolean last_line = (y == lev_fieldy - 1);
6307   boolean object_hit = FALSE;
6308   boolean impact = (last_line || object_hit);
6309   int element = Feld[x][y];
6310   int smashed = EL_STEELWALL;
6311
6312   if (!last_line)       // check if element below was hit
6313   {
6314     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6315       return;
6316
6317     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6318                                          MovDir[x][y + 1] != MV_DOWN ||
6319                                          MovPos[x][y + 1] <= TILEY / 2));
6320
6321     // do not smash moving elements that left the smashed field in time
6322     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6323         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6324       object_hit = FALSE;
6325
6326 #if USE_QUICKSAND_IMPACT_BUGFIX
6327     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6328     {
6329       RemoveMovingField(x, y + 1);
6330       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6331       Feld[x][y + 2] = EL_ROCK;
6332       TEST_DrawLevelField(x, y + 2);
6333
6334       object_hit = TRUE;
6335     }
6336
6337     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6338     {
6339       RemoveMovingField(x, y + 1);
6340       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6341       Feld[x][y + 2] = EL_ROCK;
6342       TEST_DrawLevelField(x, y + 2);
6343
6344       object_hit = TRUE;
6345     }
6346 #endif
6347
6348     if (object_hit)
6349       smashed = MovingOrBlocked2Element(x, y + 1);
6350
6351     impact = (last_line || object_hit);
6352   }
6353
6354   if (!last_line && smashed == EL_ACID) // element falls into acid
6355   {
6356     SplashAcid(x, y + 1);
6357     return;
6358   }
6359
6360   // !!! not sufficient for all cases -- see EL_PEARL below !!!
6361   // only reset graphic animation if graphic really changes after impact
6362   if (impact &&
6363       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6364   {
6365     ResetGfxAnimation(x, y);
6366     TEST_DrawLevelField(x, y);
6367   }
6368
6369   if (impact && CAN_EXPLODE_IMPACT(element))
6370   {
6371     Bang(x, y);
6372     return;
6373   }
6374   else if (impact && element == EL_PEARL &&
6375            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6376   {
6377     ResetGfxAnimation(x, y);
6378
6379     Feld[x][y] = EL_PEARL_BREAKING;
6380     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6381     return;
6382   }
6383   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6384   {
6385     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6386
6387     return;
6388   }
6389
6390   if (impact && element == EL_AMOEBA_DROP)
6391   {
6392     if (object_hit && IS_PLAYER(x, y + 1))
6393       KillPlayerUnlessEnemyProtected(x, y + 1);
6394     else if (object_hit && smashed == EL_PENGUIN)
6395       Bang(x, y + 1);
6396     else
6397     {
6398       Feld[x][y] = EL_AMOEBA_GROWING;
6399       Store[x][y] = EL_AMOEBA_WET;
6400
6401       ResetRandomAnimationValue(x, y);
6402     }
6403     return;
6404   }
6405
6406   if (object_hit)               // check which object was hit
6407   {
6408     if ((CAN_PASS_MAGIC_WALL(element) && 
6409          (smashed == EL_MAGIC_WALL ||
6410           smashed == EL_BD_MAGIC_WALL)) ||
6411         (CAN_PASS_DC_MAGIC_WALL(element) &&
6412          smashed == EL_DC_MAGIC_WALL))
6413     {
6414       int xx, yy;
6415       int activated_magic_wall =
6416         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6417          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6418          EL_DC_MAGIC_WALL_ACTIVE);
6419
6420       // activate magic wall / mill
6421       SCAN_PLAYFIELD(xx, yy)
6422       {
6423         if (Feld[xx][yy] == smashed)
6424           Feld[xx][yy] = activated_magic_wall;
6425       }
6426
6427       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6428       game.magic_wall_active = TRUE;
6429
6430       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6431                             SND_MAGIC_WALL_ACTIVATING :
6432                             smashed == EL_BD_MAGIC_WALL ?
6433                             SND_BD_MAGIC_WALL_ACTIVATING :
6434                             SND_DC_MAGIC_WALL_ACTIVATING));
6435     }
6436
6437     if (IS_PLAYER(x, y + 1))
6438     {
6439       if (CAN_SMASH_PLAYER(element))
6440       {
6441         KillPlayerUnlessEnemyProtected(x, y + 1);
6442         return;
6443       }
6444     }
6445     else if (smashed == EL_PENGUIN)
6446     {
6447       if (CAN_SMASH_PLAYER(element))
6448       {
6449         Bang(x, y + 1);
6450         return;
6451       }
6452     }
6453     else if (element == EL_BD_DIAMOND)
6454     {
6455       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6456       {
6457         Bang(x, y + 1);
6458         return;
6459       }
6460     }
6461     else if (((element == EL_SP_INFOTRON ||
6462                element == EL_SP_ZONK) &&
6463               (smashed == EL_SP_SNIKSNAK ||
6464                smashed == EL_SP_ELECTRON ||
6465                smashed == EL_SP_DISK_ORANGE)) ||
6466              (element == EL_SP_INFOTRON &&
6467               smashed == EL_SP_DISK_YELLOW))
6468     {
6469       Bang(x, y + 1);
6470       return;
6471     }
6472     else if (CAN_SMASH_EVERYTHING(element))
6473     {
6474       if (IS_CLASSIC_ENEMY(smashed) ||
6475           CAN_EXPLODE_SMASHED(smashed))
6476       {
6477         Bang(x, y + 1);
6478         return;
6479       }
6480       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6481       {
6482         if (smashed == EL_LAMP ||
6483             smashed == EL_LAMP_ACTIVE)
6484         {
6485           Bang(x, y + 1);
6486           return;
6487         }
6488         else if (smashed == EL_NUT)
6489         {
6490           Feld[x][y + 1] = EL_NUT_BREAKING;
6491           PlayLevelSound(x, y, SND_NUT_BREAKING);
6492           RaiseScoreElement(EL_NUT);
6493           return;
6494         }
6495         else if (smashed == EL_PEARL)
6496         {
6497           ResetGfxAnimation(x, y);
6498
6499           Feld[x][y + 1] = EL_PEARL_BREAKING;
6500           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6501           return;
6502         }
6503         else if (smashed == EL_DIAMOND)
6504         {
6505           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6506           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6507           return;
6508         }
6509         else if (IS_BELT_SWITCH(smashed))
6510         {
6511           ToggleBeltSwitch(x, y + 1);
6512         }
6513         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6514                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6515                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6516                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6517         {
6518           ToggleSwitchgateSwitch(x, y + 1);
6519         }
6520         else if (smashed == EL_LIGHT_SWITCH ||
6521                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6522         {
6523           ToggleLightSwitch(x, y + 1);
6524         }
6525         else
6526         {
6527           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6528
6529           CheckElementChangeBySide(x, y + 1, smashed, element,
6530                                    CE_SWITCHED, CH_SIDE_TOP);
6531           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6532                                             CH_SIDE_TOP);
6533         }
6534       }
6535       else
6536       {
6537         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6538       }
6539     }
6540   }
6541
6542   // play sound of magic wall / mill
6543   if (!last_line &&
6544       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6545        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6546        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6547   {
6548     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6549       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6550     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6551       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6552     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6553       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6554
6555     return;
6556   }
6557
6558   // play sound of object that hits the ground
6559   if (last_line || object_hit)
6560     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6561 }
6562
6563 static void TurnRoundExt(int x, int y)
6564 {
6565   static struct
6566   {
6567     int dx, dy;
6568   } move_xy[] =
6569   {
6570     {  0,  0 },
6571     { -1,  0 },
6572     { +1,  0 },
6573     {  0,  0 },
6574     {  0, -1 },
6575     {  0,  0 }, { 0, 0 }, { 0, 0 },
6576     {  0, +1 }
6577   };
6578   static struct
6579   {
6580     int left, right, back;
6581   } turn[] =
6582   {
6583     { 0,        0,              0        },
6584     { MV_DOWN,  MV_UP,          MV_RIGHT },
6585     { MV_UP,    MV_DOWN,        MV_LEFT  },
6586     { 0,        0,              0        },
6587     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6588     { 0,        0,              0        },
6589     { 0,        0,              0        },
6590     { 0,        0,              0        },
6591     { MV_RIGHT, MV_LEFT,        MV_UP    }
6592   };
6593
6594   int element = Feld[x][y];
6595   int move_pattern = element_info[element].move_pattern;
6596
6597   int old_move_dir = MovDir[x][y];
6598   int left_dir  = turn[old_move_dir].left;
6599   int right_dir = turn[old_move_dir].right;
6600   int back_dir  = turn[old_move_dir].back;
6601
6602   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6603   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6604   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6605   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6606
6607   int left_x  = x + left_dx,  left_y  = y + left_dy;
6608   int right_x = x + right_dx, right_y = y + right_dy;
6609   int move_x  = x + move_dx,  move_y  = y + move_dy;
6610
6611   int xx, yy;
6612
6613   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6614   {
6615     TestIfBadThingTouchesOtherBadThing(x, y);
6616
6617     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6618       MovDir[x][y] = right_dir;
6619     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6620       MovDir[x][y] = left_dir;
6621
6622     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6623       MovDelay[x][y] = 9;
6624     else if (element == EL_BD_BUTTERFLY)     // && MovDir[x][y] == left_dir)
6625       MovDelay[x][y] = 1;
6626   }
6627   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6628   {
6629     TestIfBadThingTouchesOtherBadThing(x, y);
6630
6631     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6632       MovDir[x][y] = left_dir;
6633     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6634       MovDir[x][y] = right_dir;
6635
6636     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6637       MovDelay[x][y] = 9;
6638     else if (element == EL_BD_FIREFLY)      // && MovDir[x][y] == right_dir)
6639       MovDelay[x][y] = 1;
6640   }
6641   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6642   {
6643     TestIfBadThingTouchesOtherBadThing(x, y);
6644
6645     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6646       MovDir[x][y] = left_dir;
6647     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6648       MovDir[x][y] = right_dir;
6649
6650     if (MovDir[x][y] != old_move_dir)
6651       MovDelay[x][y] = 9;
6652   }
6653   else if (element == EL_YAMYAM)
6654   {
6655     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6656     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6657
6658     if (can_turn_left && can_turn_right)
6659       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6660     else if (can_turn_left)
6661       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6662     else if (can_turn_right)
6663       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6664     else
6665       MovDir[x][y] = back_dir;
6666
6667     MovDelay[x][y] = 16 + 16 * RND(3);
6668   }
6669   else if (element == EL_DARK_YAMYAM)
6670   {
6671     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6672                                                          left_x, left_y);
6673     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6674                                                          right_x, right_y);
6675
6676     if (can_turn_left && can_turn_right)
6677       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6678     else if (can_turn_left)
6679       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6680     else if (can_turn_right)
6681       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6682     else
6683       MovDir[x][y] = back_dir;
6684
6685     MovDelay[x][y] = 16 + 16 * RND(3);
6686   }
6687   else if (element == EL_PACMAN)
6688   {
6689     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6690     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6691
6692     if (can_turn_left && can_turn_right)
6693       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6694     else if (can_turn_left)
6695       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6696     else if (can_turn_right)
6697       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6698     else
6699       MovDir[x][y] = back_dir;
6700
6701     MovDelay[x][y] = 6 + RND(40);
6702   }
6703   else if (element == EL_PIG)
6704   {
6705     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6706     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6707     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6708     boolean should_turn_left, should_turn_right, should_move_on;
6709     int rnd_value = 24;
6710     int rnd = RND(rnd_value);
6711
6712     should_turn_left = (can_turn_left &&
6713                         (!can_move_on ||
6714                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6715                                                    y + back_dy + left_dy)));
6716     should_turn_right = (can_turn_right &&
6717                          (!can_move_on ||
6718                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6719                                                     y + back_dy + right_dy)));
6720     should_move_on = (can_move_on &&
6721                       (!can_turn_left ||
6722                        !can_turn_right ||
6723                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6724                                                  y + move_dy + left_dy) ||
6725                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6726                                                  y + move_dy + right_dy)));
6727
6728     if (should_turn_left || should_turn_right || should_move_on)
6729     {
6730       if (should_turn_left && should_turn_right && should_move_on)
6731         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6732                         rnd < 2 * rnd_value / 3 ? right_dir :
6733                         old_move_dir);
6734       else if (should_turn_left && should_turn_right)
6735         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6736       else if (should_turn_left && should_move_on)
6737         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6738       else if (should_turn_right && should_move_on)
6739         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6740       else if (should_turn_left)
6741         MovDir[x][y] = left_dir;
6742       else if (should_turn_right)
6743         MovDir[x][y] = right_dir;
6744       else if (should_move_on)
6745         MovDir[x][y] = old_move_dir;
6746     }
6747     else if (can_move_on && rnd > rnd_value / 8)
6748       MovDir[x][y] = old_move_dir;
6749     else if (can_turn_left && can_turn_right)
6750       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6751     else if (can_turn_left && rnd > rnd_value / 8)
6752       MovDir[x][y] = left_dir;
6753     else if (can_turn_right && rnd > rnd_value/8)
6754       MovDir[x][y] = right_dir;
6755     else
6756       MovDir[x][y] = back_dir;
6757
6758     xx = x + move_xy[MovDir[x][y]].dx;
6759     yy = y + move_xy[MovDir[x][y]].dy;
6760
6761     if (!IN_LEV_FIELD(xx, yy) ||
6762         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6763       MovDir[x][y] = old_move_dir;
6764
6765     MovDelay[x][y] = 0;
6766   }
6767   else if (element == EL_DRAGON)
6768   {
6769     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6770     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6771     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6772     int rnd_value = 24;
6773     int rnd = RND(rnd_value);
6774
6775     if (can_move_on && rnd > rnd_value / 8)
6776       MovDir[x][y] = old_move_dir;
6777     else if (can_turn_left && can_turn_right)
6778       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6779     else if (can_turn_left && rnd > rnd_value / 8)
6780       MovDir[x][y] = left_dir;
6781     else if (can_turn_right && rnd > rnd_value / 8)
6782       MovDir[x][y] = right_dir;
6783     else
6784       MovDir[x][y] = back_dir;
6785
6786     xx = x + move_xy[MovDir[x][y]].dx;
6787     yy = y + move_xy[MovDir[x][y]].dy;
6788
6789     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6790       MovDir[x][y] = old_move_dir;
6791
6792     MovDelay[x][y] = 0;
6793   }
6794   else if (element == EL_MOLE)
6795   {
6796     boolean can_move_on =
6797       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6798                             IS_AMOEBOID(Feld[move_x][move_y]) ||
6799                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6800     if (!can_move_on)
6801     {
6802       boolean can_turn_left =
6803         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6804                               IS_AMOEBOID(Feld[left_x][left_y])));
6805
6806       boolean can_turn_right =
6807         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6808                               IS_AMOEBOID(Feld[right_x][right_y])));
6809
6810       if (can_turn_left && can_turn_right)
6811         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6812       else if (can_turn_left)
6813         MovDir[x][y] = left_dir;
6814       else
6815         MovDir[x][y] = right_dir;
6816     }
6817
6818     if (MovDir[x][y] != old_move_dir)
6819       MovDelay[x][y] = 9;
6820   }
6821   else if (element == EL_BALLOON)
6822   {
6823     MovDir[x][y] = game.wind_direction;
6824     MovDelay[x][y] = 0;
6825   }
6826   else if (element == EL_SPRING)
6827   {
6828     if (MovDir[x][y] & MV_HORIZONTAL)
6829     {
6830       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6831           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6832       {
6833         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6834         ResetGfxAnimation(move_x, move_y);
6835         TEST_DrawLevelField(move_x, move_y);
6836
6837         MovDir[x][y] = back_dir;
6838       }
6839       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6840                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6841         MovDir[x][y] = MV_NONE;
6842     }
6843
6844     MovDelay[x][y] = 0;
6845   }
6846   else if (element == EL_ROBOT ||
6847            element == EL_SATELLITE ||
6848            element == EL_PENGUIN ||
6849            element == EL_EMC_ANDROID)
6850   {
6851     int attr_x = -1, attr_y = -1;
6852
6853     if (game.all_players_gone)
6854     {
6855       attr_x = game.exit_x;
6856       attr_y = game.exit_y;
6857     }
6858     else
6859     {
6860       int i;
6861
6862       for (i = 0; i < MAX_PLAYERS; i++)
6863       {
6864         struct PlayerInfo *player = &stored_player[i];
6865         int jx = player->jx, jy = player->jy;
6866
6867         if (!player->active)
6868           continue;
6869
6870         if (attr_x == -1 ||
6871             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6872         {
6873           attr_x = jx;
6874           attr_y = jy;
6875         }
6876       }
6877     }
6878
6879     if (element == EL_ROBOT &&
6880         game.robot_wheel_x >= 0 &&
6881         game.robot_wheel_y >= 0 &&
6882         (Feld[game.robot_wheel_x][game.robot_wheel_y] == EL_ROBOT_WHEEL_ACTIVE ||
6883          game.engine_version < VERSION_IDENT(3,1,0,0)))
6884     {
6885       attr_x = game.robot_wheel_x;
6886       attr_y = game.robot_wheel_y;
6887     }
6888
6889     if (element == EL_PENGUIN)
6890     {
6891       int i;
6892       static int xy[4][2] =
6893       {
6894         { 0, -1 },
6895         { -1, 0 },
6896         { +1, 0 },
6897         { 0, +1 }
6898       };
6899
6900       for (i = 0; i < NUM_DIRECTIONS; i++)
6901       {
6902         int ex = x + xy[i][0];
6903         int ey = y + xy[i][1];
6904
6905         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6906                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6907                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6908                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6909         {
6910           attr_x = ex;
6911           attr_y = ey;
6912           break;
6913         }
6914       }
6915     }
6916
6917     MovDir[x][y] = MV_NONE;
6918     if (attr_x < x)
6919       MovDir[x][y] |= (game.all_players_gone ? MV_RIGHT : MV_LEFT);
6920     else if (attr_x > x)
6921       MovDir[x][y] |= (game.all_players_gone ? MV_LEFT : MV_RIGHT);
6922     if (attr_y < y)
6923       MovDir[x][y] |= (game.all_players_gone ? MV_DOWN : MV_UP);
6924     else if (attr_y > y)
6925       MovDir[x][y] |= (game.all_players_gone ? MV_UP : MV_DOWN);
6926
6927     if (element == EL_ROBOT)
6928     {
6929       int newx, newy;
6930
6931       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6932         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6933       Moving2Blocked(x, y, &newx, &newy);
6934
6935       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6936         MovDelay[x][y] = 8 + 8 * !RND(3);
6937       else
6938         MovDelay[x][y] = 16;
6939     }
6940     else if (element == EL_PENGUIN)
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 (PENGUIN_CAN_ENTER_FIELD(element, 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 (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6963           return;
6964
6965         MovDir[x][y] = old_move_dir;
6966         return;
6967       }
6968     }
6969     else if (element == EL_SATELLITE)
6970     {
6971       int newx, newy;
6972
6973       MovDelay[x][y] = 1;
6974
6975       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6976       {
6977         boolean first_horiz = RND(2);
6978         int new_move_dir = MovDir[x][y];
6979
6980         MovDir[x][y] =
6981           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6982         Moving2Blocked(x, y, &newx, &newy);
6983
6984         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6985           return;
6986
6987         MovDir[x][y] =
6988           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6989         Moving2Blocked(x, y, &newx, &newy);
6990
6991         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6992           return;
6993
6994         MovDir[x][y] = old_move_dir;
6995         return;
6996       }
6997     }
6998     else if (element == EL_EMC_ANDROID)
6999     {
7000       static int check_pos[16] =
7001       {
7002         -1,             //  0 => (invalid)
7003         7,              //  1 => MV_LEFT
7004         3,              //  2 => MV_RIGHT
7005         -1,             //  3 => (invalid)
7006         1,              //  4 =>            MV_UP
7007         0,              //  5 => MV_LEFT  | MV_UP
7008         2,              //  6 => MV_RIGHT | MV_UP
7009         -1,             //  7 => (invalid)
7010         5,              //  8 =>            MV_DOWN
7011         6,              //  9 => MV_LEFT  | MV_DOWN
7012         4,              // 10 => MV_RIGHT | MV_DOWN
7013         -1,             // 11 => (invalid)
7014         -1,             // 12 => (invalid)
7015         -1,             // 13 => (invalid)
7016         -1,             // 14 => (invalid)
7017         -1,             // 15 => (invalid)
7018       };
7019       static struct
7020       {
7021         int dx, dy;
7022         int dir;
7023       } check_xy[8] =
7024       {
7025         { -1, -1,       MV_LEFT  | MV_UP   },
7026         {  0, -1,                  MV_UP   },
7027         { +1, -1,       MV_RIGHT | MV_UP   },
7028         { +1,  0,       MV_RIGHT           },
7029         { +1, +1,       MV_RIGHT | MV_DOWN },
7030         {  0, +1,                  MV_DOWN },
7031         { -1, +1,       MV_LEFT  | MV_DOWN },
7032         { -1,  0,       MV_LEFT            },
7033       };
7034       int start_pos, check_order;
7035       boolean can_clone = FALSE;
7036       int i;
7037
7038       // check if there is any free field around current position
7039       for (i = 0; i < 8; i++)
7040       {
7041         int newx = x + check_xy[i].dx;
7042         int newy = y + check_xy[i].dy;
7043
7044         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7045         {
7046           can_clone = TRUE;
7047
7048           break;
7049         }
7050       }
7051
7052       if (can_clone)            // randomly find an element to clone
7053       {
7054         can_clone = FALSE;
7055
7056         start_pos = check_pos[RND(8)];
7057         check_order = (RND(2) ? -1 : +1);
7058
7059         for (i = 0; i < 8; i++)
7060         {
7061           int pos_raw = start_pos + i * check_order;
7062           int pos = (pos_raw + 8) % 8;
7063           int newx = x + check_xy[pos].dx;
7064           int newy = y + check_xy[pos].dy;
7065
7066           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7067           {
7068             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7069             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7070
7071             Store[x][y] = Feld[newx][newy];
7072
7073             can_clone = TRUE;
7074
7075             break;
7076           }
7077         }
7078       }
7079
7080       if (can_clone)            // randomly find a direction to move
7081       {
7082         can_clone = FALSE;
7083
7084         start_pos = check_pos[RND(8)];
7085         check_order = (RND(2) ? -1 : +1);
7086
7087         for (i = 0; i < 8; i++)
7088         {
7089           int pos_raw = start_pos + i * 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 (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7096           {
7097             MovDir[x][y] = new_move_dir;
7098             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7099
7100             can_clone = TRUE;
7101
7102             break;
7103           }
7104         }
7105       }
7106
7107       if (can_clone)            // cloning and moving successful
7108         return;
7109
7110       // cannot clone -- try to move towards player
7111
7112       start_pos = check_pos[MovDir[x][y] & 0x0f];
7113       check_order = (RND(2) ? -1 : +1);
7114
7115       for (i = 0; i < 3; i++)
7116       {
7117         // first check start_pos, then previous/next or (next/previous) pos
7118         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7119         int pos = (pos_raw + 8) % 8;
7120         int newx = x + check_xy[pos].dx;
7121         int newy = y + check_xy[pos].dy;
7122         int new_move_dir = check_xy[pos].dir;
7123
7124         if (IS_PLAYER(newx, newy))
7125           break;
7126
7127         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7128         {
7129           MovDir[x][y] = new_move_dir;
7130           MovDelay[x][y] = level.android_move_time * 8 + 1;
7131
7132           break;
7133         }
7134       }
7135     }
7136   }
7137   else if (move_pattern == MV_TURNING_LEFT ||
7138            move_pattern == MV_TURNING_RIGHT ||
7139            move_pattern == MV_TURNING_LEFT_RIGHT ||
7140            move_pattern == MV_TURNING_RIGHT_LEFT ||
7141            move_pattern == MV_TURNING_RANDOM ||
7142            move_pattern == MV_ALL_DIRECTIONS)
7143   {
7144     boolean can_turn_left =
7145       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7146     boolean can_turn_right =
7147       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7148
7149     if (element_info[element].move_stepsize == 0)       // "not moving"
7150       return;
7151
7152     if (move_pattern == MV_TURNING_LEFT)
7153       MovDir[x][y] = left_dir;
7154     else if (move_pattern == MV_TURNING_RIGHT)
7155       MovDir[x][y] = right_dir;
7156     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7157       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7158     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7159       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7160     else if (move_pattern == MV_TURNING_RANDOM)
7161       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7162                       can_turn_right && !can_turn_left ? right_dir :
7163                       RND(2) ? left_dir : right_dir);
7164     else if (can_turn_left && can_turn_right)
7165       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7166     else if (can_turn_left)
7167       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7168     else if (can_turn_right)
7169       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7170     else
7171       MovDir[x][y] = back_dir;
7172
7173     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7174   }
7175   else if (move_pattern == MV_HORIZONTAL ||
7176            move_pattern == MV_VERTICAL)
7177   {
7178     if (move_pattern & old_move_dir)
7179       MovDir[x][y] = back_dir;
7180     else if (move_pattern == MV_HORIZONTAL)
7181       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7182     else if (move_pattern == MV_VERTICAL)
7183       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7184
7185     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7186   }
7187   else if (move_pattern & MV_ANY_DIRECTION)
7188   {
7189     MovDir[x][y] = move_pattern;
7190     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7191   }
7192   else if (move_pattern & MV_WIND_DIRECTION)
7193   {
7194     MovDir[x][y] = game.wind_direction;
7195     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7196   }
7197   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7198   {
7199     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7200       MovDir[x][y] = left_dir;
7201     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7202       MovDir[x][y] = right_dir;
7203
7204     if (MovDir[x][y] != old_move_dir)
7205       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7206   }
7207   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7208   {
7209     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7210       MovDir[x][y] = right_dir;
7211     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7212       MovDir[x][y] = left_dir;
7213
7214     if (MovDir[x][y] != old_move_dir)
7215       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7216   }
7217   else if (move_pattern == MV_TOWARDS_PLAYER ||
7218            move_pattern == MV_AWAY_FROM_PLAYER)
7219   {
7220     int attr_x = -1, attr_y = -1;
7221     int newx, newy;
7222     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7223
7224     if (game.all_players_gone)
7225     {
7226       attr_x = game.exit_x;
7227       attr_y = game.exit_y;
7228     }
7229     else
7230     {
7231       int i;
7232
7233       for (i = 0; i < MAX_PLAYERS; i++)
7234       {
7235         struct PlayerInfo *player = &stored_player[i];
7236         int jx = player->jx, jy = player->jy;
7237
7238         if (!player->active)
7239           continue;
7240
7241         if (attr_x == -1 ||
7242             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7243         {
7244           attr_x = jx;
7245           attr_y = jy;
7246         }
7247       }
7248     }
7249
7250     MovDir[x][y] = MV_NONE;
7251     if (attr_x < x)
7252       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7253     else if (attr_x > x)
7254       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7255     if (attr_y < y)
7256       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7257     else if (attr_y > y)
7258       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7259
7260     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7261
7262     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7263     {
7264       boolean first_horiz = RND(2);
7265       int new_move_dir = MovDir[x][y];
7266
7267       if (element_info[element].move_stepsize == 0)     // "not moving"
7268       {
7269         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7270         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7271
7272         return;
7273       }
7274
7275       MovDir[x][y] =
7276         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7277       Moving2Blocked(x, y, &newx, &newy);
7278
7279       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7280         return;
7281
7282       MovDir[x][y] =
7283         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7284       Moving2Blocked(x, y, &newx, &newy);
7285
7286       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7287         return;
7288
7289       MovDir[x][y] = old_move_dir;
7290     }
7291   }
7292   else if (move_pattern == MV_WHEN_PUSHED ||
7293            move_pattern == MV_WHEN_DROPPED)
7294   {
7295     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7296       MovDir[x][y] = MV_NONE;
7297
7298     MovDelay[x][y] = 0;
7299   }
7300   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7301   {
7302     static int test_xy[7][2] =
7303     {
7304       { 0, -1 },
7305       { -1, 0 },
7306       { +1, 0 },
7307       { 0, +1 },
7308       { 0, -1 },
7309       { -1, 0 },
7310       { +1, 0 },
7311     };
7312     static int test_dir[7] =
7313     {
7314       MV_UP,
7315       MV_LEFT,
7316       MV_RIGHT,
7317       MV_DOWN,
7318       MV_UP,
7319       MV_LEFT,
7320       MV_RIGHT,
7321     };
7322     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7323     int move_preference = -1000000;     // start with very low preference
7324     int new_move_dir = MV_NONE;
7325     int start_test = RND(4);
7326     int i;
7327
7328     for (i = 0; i < NUM_DIRECTIONS; i++)
7329     {
7330       int move_dir = test_dir[start_test + i];
7331       int move_dir_preference;
7332
7333       xx = x + test_xy[start_test + i][0];
7334       yy = y + test_xy[start_test + i][1];
7335
7336       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7337           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7338       {
7339         new_move_dir = move_dir;
7340
7341         break;
7342       }
7343
7344       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7345         continue;
7346
7347       move_dir_preference = -1 * RunnerVisit[xx][yy];
7348       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7349         move_dir_preference = PlayerVisit[xx][yy];
7350
7351       if (move_dir_preference > move_preference)
7352       {
7353         // prefer field that has not been visited for the longest time
7354         move_preference = move_dir_preference;
7355         new_move_dir = move_dir;
7356       }
7357       else if (move_dir_preference == move_preference &&
7358                move_dir == old_move_dir)
7359       {
7360         // prefer last direction when all directions are preferred equally
7361         move_preference = move_dir_preference;
7362         new_move_dir = move_dir;
7363       }
7364     }
7365
7366     MovDir[x][y] = new_move_dir;
7367     if (old_move_dir != new_move_dir)
7368       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7369   }
7370 }
7371
7372 static void TurnRound(int x, int y)
7373 {
7374   int direction = MovDir[x][y];
7375
7376   TurnRoundExt(x, y);
7377
7378   GfxDir[x][y] = MovDir[x][y];
7379
7380   if (direction != MovDir[x][y])
7381     GfxFrame[x][y] = 0;
7382
7383   if (MovDelay[x][y])
7384     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7385
7386   ResetGfxFrame(x, y);
7387 }
7388
7389 static boolean JustBeingPushed(int x, int y)
7390 {
7391   int i;
7392
7393   for (i = 0; i < MAX_PLAYERS; i++)
7394   {
7395     struct PlayerInfo *player = &stored_player[i];
7396
7397     if (player->active && player->is_pushing && player->MovPos)
7398     {
7399       int next_jx = player->jx + (player->jx - player->last_jx);
7400       int next_jy = player->jy + (player->jy - player->last_jy);
7401
7402       if (x == next_jx && y == next_jy)
7403         return TRUE;
7404     }
7405   }
7406
7407   return FALSE;
7408 }
7409
7410 static void StartMoving(int x, int y)
7411 {
7412   boolean started_moving = FALSE;       // some elements can fall _and_ move
7413   int element = Feld[x][y];
7414
7415   if (Stop[x][y])
7416     return;
7417
7418   if (MovDelay[x][y] == 0)
7419     GfxAction[x][y] = ACTION_DEFAULT;
7420
7421   if (CAN_FALL(element) && y < lev_fieldy - 1)
7422   {
7423     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7424         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7425       if (JustBeingPushed(x, y))
7426         return;
7427
7428     if (element == EL_QUICKSAND_FULL)
7429     {
7430       if (IS_FREE(x, y + 1))
7431       {
7432         InitMovingField(x, y, MV_DOWN);
7433         started_moving = TRUE;
7434
7435         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7436 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7437         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7438           Store[x][y] = EL_ROCK;
7439 #else
7440         Store[x][y] = EL_ROCK;
7441 #endif
7442
7443         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7444       }
7445       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7446       {
7447         if (!MovDelay[x][y])
7448         {
7449           MovDelay[x][y] = TILEY + 1;
7450
7451           ResetGfxAnimation(x, y);
7452           ResetGfxAnimation(x, y + 1);
7453         }
7454
7455         if (MovDelay[x][y])
7456         {
7457           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7458           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7459
7460           MovDelay[x][y]--;
7461           if (MovDelay[x][y])
7462             return;
7463         }
7464
7465         Feld[x][y] = EL_QUICKSAND_EMPTY;
7466         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7467         Store[x][y + 1] = Store[x][y];
7468         Store[x][y] = 0;
7469
7470         PlayLevelSoundAction(x, y, ACTION_FILLING);
7471       }
7472       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7473       {
7474         if (!MovDelay[x][y])
7475         {
7476           MovDelay[x][y] = TILEY + 1;
7477
7478           ResetGfxAnimation(x, y);
7479           ResetGfxAnimation(x, y + 1);
7480         }
7481
7482         if (MovDelay[x][y])
7483         {
7484           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7485           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7486
7487           MovDelay[x][y]--;
7488           if (MovDelay[x][y])
7489             return;
7490         }
7491
7492         Feld[x][y] = EL_QUICKSAND_EMPTY;
7493         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7494         Store[x][y + 1] = Store[x][y];
7495         Store[x][y] = 0;
7496
7497         PlayLevelSoundAction(x, y, ACTION_FILLING);
7498       }
7499     }
7500     else if (element == EL_QUICKSAND_FAST_FULL)
7501     {
7502       if (IS_FREE(x, y + 1))
7503       {
7504         InitMovingField(x, y, MV_DOWN);
7505         started_moving = TRUE;
7506
7507         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7508 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7509         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7510           Store[x][y] = EL_ROCK;
7511 #else
7512         Store[x][y] = EL_ROCK;
7513 #endif
7514
7515         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7516       }
7517       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7518       {
7519         if (!MovDelay[x][y])
7520         {
7521           MovDelay[x][y] = TILEY + 1;
7522
7523           ResetGfxAnimation(x, y);
7524           ResetGfxAnimation(x, y + 1);
7525         }
7526
7527         if (MovDelay[x][y])
7528         {
7529           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7530           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7531
7532           MovDelay[x][y]--;
7533           if (MovDelay[x][y])
7534             return;
7535         }
7536
7537         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7538         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7539         Store[x][y + 1] = Store[x][y];
7540         Store[x][y] = 0;
7541
7542         PlayLevelSoundAction(x, y, ACTION_FILLING);
7543       }
7544       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7545       {
7546         if (!MovDelay[x][y])
7547         {
7548           MovDelay[x][y] = TILEY + 1;
7549
7550           ResetGfxAnimation(x, y);
7551           ResetGfxAnimation(x, y + 1);
7552         }
7553
7554         if (MovDelay[x][y])
7555         {
7556           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7557           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7558
7559           MovDelay[x][y]--;
7560           if (MovDelay[x][y])
7561             return;
7562         }
7563
7564         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7565         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7566         Store[x][y + 1] = Store[x][y];
7567         Store[x][y] = 0;
7568
7569         PlayLevelSoundAction(x, y, ACTION_FILLING);
7570       }
7571     }
7572     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7573              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7574     {
7575       InitMovingField(x, y, MV_DOWN);
7576       started_moving = TRUE;
7577
7578       Feld[x][y] = EL_QUICKSAND_FILLING;
7579       Store[x][y] = element;
7580
7581       PlayLevelSoundAction(x, y, ACTION_FILLING);
7582     }
7583     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7584              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7585     {
7586       InitMovingField(x, y, MV_DOWN);
7587       started_moving = TRUE;
7588
7589       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7590       Store[x][y] = element;
7591
7592       PlayLevelSoundAction(x, y, ACTION_FILLING);
7593     }
7594     else if (element == EL_MAGIC_WALL_FULL)
7595     {
7596       if (IS_FREE(x, y + 1))
7597       {
7598         InitMovingField(x, y, MV_DOWN);
7599         started_moving = TRUE;
7600
7601         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7602         Store[x][y] = EL_CHANGED(Store[x][y]);
7603       }
7604       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7605       {
7606         if (!MovDelay[x][y])
7607           MovDelay[x][y] = TILEY / 4 + 1;
7608
7609         if (MovDelay[x][y])
7610         {
7611           MovDelay[x][y]--;
7612           if (MovDelay[x][y])
7613             return;
7614         }
7615
7616         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7617         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7618         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7619         Store[x][y] = 0;
7620       }
7621     }
7622     else if (element == EL_BD_MAGIC_WALL_FULL)
7623     {
7624       if (IS_FREE(x, y + 1))
7625       {
7626         InitMovingField(x, y, MV_DOWN);
7627         started_moving = TRUE;
7628
7629         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7630         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7631       }
7632       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7633       {
7634         if (!MovDelay[x][y])
7635           MovDelay[x][y] = TILEY / 4 + 1;
7636
7637         if (MovDelay[x][y])
7638         {
7639           MovDelay[x][y]--;
7640           if (MovDelay[x][y])
7641             return;
7642         }
7643
7644         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7645         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7646         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7647         Store[x][y] = 0;
7648       }
7649     }
7650     else if (element == EL_DC_MAGIC_WALL_FULL)
7651     {
7652       if (IS_FREE(x, y + 1))
7653       {
7654         InitMovingField(x, y, MV_DOWN);
7655         started_moving = TRUE;
7656
7657         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7658         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7659       }
7660       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7661       {
7662         if (!MovDelay[x][y])
7663           MovDelay[x][y] = TILEY / 4 + 1;
7664
7665         if (MovDelay[x][y])
7666         {
7667           MovDelay[x][y]--;
7668           if (MovDelay[x][y])
7669             return;
7670         }
7671
7672         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7673         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7674         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7675         Store[x][y] = 0;
7676       }
7677     }
7678     else if ((CAN_PASS_MAGIC_WALL(element) &&
7679               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7680                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7681              (CAN_PASS_DC_MAGIC_WALL(element) &&
7682               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7683
7684     {
7685       InitMovingField(x, y, MV_DOWN);
7686       started_moving = TRUE;
7687
7688       Feld[x][y] =
7689         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7690          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7691          EL_DC_MAGIC_WALL_FILLING);
7692       Store[x][y] = element;
7693     }
7694     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7695     {
7696       SplashAcid(x, y + 1);
7697
7698       InitMovingField(x, y, MV_DOWN);
7699       started_moving = TRUE;
7700
7701       Store[x][y] = EL_ACID;
7702     }
7703     else if (
7704              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7705               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7706              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7707               CAN_FALL(element) && WasJustFalling[x][y] &&
7708               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7709
7710              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7711               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7712               (Feld[x][y + 1] == EL_BLOCKED)))
7713     {
7714       /* this is needed for a special case not covered by calling "Impact()"
7715          from "ContinueMoving()": if an element moves to a tile directly below
7716          another element which was just falling on that tile (which was empty
7717          in the previous frame), the falling element above would just stop
7718          instead of smashing the element below (in previous version, the above
7719          element was just checked for "moving" instead of "falling", resulting
7720          in incorrect smashes caused by horizontal movement of the above
7721          element; also, the case of the player being the element to smash was
7722          simply not covered here... :-/ ) */
7723
7724       CheckCollision[x][y] = 0;
7725       CheckImpact[x][y] = 0;
7726
7727       Impact(x, y);
7728     }
7729     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7730     {
7731       if (MovDir[x][y] == MV_NONE)
7732       {
7733         InitMovingField(x, y, MV_DOWN);
7734         started_moving = TRUE;
7735       }
7736     }
7737     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7738     {
7739       if (WasJustFalling[x][y]) // prevent animation from being restarted
7740         MovDir[x][y] = MV_DOWN;
7741
7742       InitMovingField(x, y, MV_DOWN);
7743       started_moving = TRUE;
7744     }
7745     else if (element == EL_AMOEBA_DROP)
7746     {
7747       Feld[x][y] = EL_AMOEBA_GROWING;
7748       Store[x][y] = EL_AMOEBA_WET;
7749     }
7750     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7751               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7752              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7753              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7754     {
7755       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7756                                 (IS_FREE(x - 1, y + 1) ||
7757                                  Feld[x - 1][y + 1] == EL_ACID));
7758       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7759                                 (IS_FREE(x + 1, y + 1) ||
7760                                  Feld[x + 1][y + 1] == EL_ACID));
7761       boolean can_fall_any  = (can_fall_left || can_fall_right);
7762       boolean can_fall_both = (can_fall_left && can_fall_right);
7763       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7764
7765       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7766       {
7767         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7768           can_fall_right = FALSE;
7769         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7770           can_fall_left = FALSE;
7771         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7772           can_fall_right = FALSE;
7773         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7774           can_fall_left = FALSE;
7775
7776         can_fall_any  = (can_fall_left || can_fall_right);
7777         can_fall_both = FALSE;
7778       }
7779
7780       if (can_fall_both)
7781       {
7782         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7783           can_fall_right = FALSE;       // slip down on left side
7784         else
7785           can_fall_left = !(can_fall_right = RND(2));
7786
7787         can_fall_both = FALSE;
7788       }
7789
7790       if (can_fall_any)
7791       {
7792         // if not determined otherwise, prefer left side for slipping down
7793         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7794         started_moving = TRUE;
7795       }
7796     }
7797     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7798     {
7799       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
7800       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7801       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7802       int belt_dir = game.belt_dir[belt_nr];
7803
7804       if ((belt_dir == MV_LEFT  && left_is_free) ||
7805           (belt_dir == MV_RIGHT && right_is_free))
7806       {
7807         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7808
7809         InitMovingField(x, y, belt_dir);
7810         started_moving = TRUE;
7811
7812         Pushed[x][y] = TRUE;
7813         Pushed[nextx][y] = TRUE;
7814
7815         GfxAction[x][y] = ACTION_DEFAULT;
7816       }
7817       else
7818       {
7819         MovDir[x][y] = 0;       // if element was moving, stop it
7820       }
7821     }
7822   }
7823
7824   // not "else if" because of elements that can fall and move (EL_SPRING)
7825   if (CAN_MOVE(element) && !started_moving)
7826   {
7827     int move_pattern = element_info[element].move_pattern;
7828     int newx, newy;
7829
7830     Moving2Blocked(x, y, &newx, &newy);
7831
7832     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7833       return;
7834
7835     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7836         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7837     {
7838       WasJustMoving[x][y] = 0;
7839       CheckCollision[x][y] = 0;
7840
7841       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7842
7843       if (Feld[x][y] != element)        // element has changed
7844         return;
7845     }
7846
7847     if (!MovDelay[x][y])        // start new movement phase
7848     {
7849       // all objects that can change their move direction after each step
7850       // (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall
7851
7852       if (element != EL_YAMYAM &&
7853           element != EL_DARK_YAMYAM &&
7854           element != EL_PACMAN &&
7855           !(move_pattern & MV_ANY_DIRECTION) &&
7856           move_pattern != MV_TURNING_LEFT &&
7857           move_pattern != MV_TURNING_RIGHT &&
7858           move_pattern != MV_TURNING_LEFT_RIGHT &&
7859           move_pattern != MV_TURNING_RIGHT_LEFT &&
7860           move_pattern != MV_TURNING_RANDOM)
7861       {
7862         TurnRound(x, y);
7863
7864         if (MovDelay[x][y] && (element == EL_BUG ||
7865                                element == EL_SPACESHIP ||
7866                                element == EL_SP_SNIKSNAK ||
7867                                element == EL_SP_ELECTRON ||
7868                                element == EL_MOLE))
7869           TEST_DrawLevelField(x, y);
7870       }
7871     }
7872
7873     if (MovDelay[x][y])         // wait some time before next movement
7874     {
7875       MovDelay[x][y]--;
7876
7877       if (element == EL_ROBOT ||
7878           element == EL_YAMYAM ||
7879           element == EL_DARK_YAMYAM)
7880       {
7881         DrawLevelElementAnimationIfNeeded(x, y, element);
7882         PlayLevelSoundAction(x, y, ACTION_WAITING);
7883       }
7884       else if (element == EL_SP_ELECTRON)
7885         DrawLevelElementAnimationIfNeeded(x, y, element);
7886       else if (element == EL_DRAGON)
7887       {
7888         int i;
7889         int dir = MovDir[x][y];
7890         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7891         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
7892         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
7893                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
7894                        dir == MV_UP     ? IMG_FLAMES_1_UP :
7895                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7896         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7897
7898         GfxAction[x][y] = ACTION_ATTACKING;
7899
7900         if (IS_PLAYER(x, y))
7901           DrawPlayerField(x, y);
7902         else
7903           TEST_DrawLevelField(x, y);
7904
7905         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7906
7907         for (i = 1; i <= 3; i++)
7908         {
7909           int xx = x + i * dx;
7910           int yy = y + i * dy;
7911           int sx = SCREENX(xx);
7912           int sy = SCREENY(yy);
7913           int flame_graphic = graphic + (i - 1);
7914
7915           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7916             break;
7917
7918           if (MovDelay[x][y])
7919           {
7920             int flamed = MovingOrBlocked2Element(xx, yy);
7921
7922             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7923               Bang(xx, yy);
7924             else
7925               RemoveMovingField(xx, yy);
7926
7927             ChangeDelay[xx][yy] = 0;
7928
7929             Feld[xx][yy] = EL_FLAMES;
7930
7931             if (IN_SCR_FIELD(sx, sy))
7932             {
7933               TEST_DrawLevelFieldCrumbled(xx, yy);
7934               DrawGraphic(sx, sy, flame_graphic, frame);
7935             }
7936           }
7937           else
7938           {
7939             if (Feld[xx][yy] == EL_FLAMES)
7940               Feld[xx][yy] = EL_EMPTY;
7941             TEST_DrawLevelField(xx, yy);
7942           }
7943         }
7944       }
7945
7946       if (MovDelay[x][y])       // element still has to wait some time
7947       {
7948         PlayLevelSoundAction(x, y, ACTION_WAITING);
7949
7950         return;
7951       }
7952     }
7953
7954     // now make next step
7955
7956     Moving2Blocked(x, y, &newx, &newy); // get next screen position
7957
7958     if (DONT_COLLIDE_WITH(element) &&
7959         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7960         !PLAYER_ENEMY_PROTECTED(newx, newy))
7961     {
7962       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7963
7964       return;
7965     }
7966
7967     else if (CAN_MOVE_INTO_ACID(element) &&
7968              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7969              !IS_MV_DIAGONAL(MovDir[x][y]) &&
7970              (MovDir[x][y] == MV_DOWN ||
7971               game.engine_version >= VERSION_IDENT(3,1,0,0)))
7972     {
7973       SplashAcid(newx, newy);
7974       Store[x][y] = EL_ACID;
7975     }
7976     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7977     {
7978       if (Feld[newx][newy] == EL_EXIT_OPEN ||
7979           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7980           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7981           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7982       {
7983         RemoveField(x, y);
7984         TEST_DrawLevelField(x, y);
7985
7986         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7987         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7988           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7989
7990         game.friends_still_needed--;
7991         if (!game.friends_still_needed &&
7992             !game.GameOver &&
7993             game.all_players_gone)
7994           LevelSolved();
7995
7996         return;
7997       }
7998       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7999       {
8000         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8001           TEST_DrawLevelField(newx, newy);
8002         else
8003           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8004       }
8005       else if (!IS_FREE(newx, newy))
8006       {
8007         GfxAction[x][y] = ACTION_WAITING;
8008
8009         if (IS_PLAYER(x, y))
8010           DrawPlayerField(x, y);
8011         else
8012           TEST_DrawLevelField(x, y);
8013
8014         return;
8015       }
8016     }
8017     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8018     {
8019       if (IS_FOOD_PIG(Feld[newx][newy]))
8020       {
8021         if (IS_MOVING(newx, newy))
8022           RemoveMovingField(newx, newy);
8023         else
8024         {
8025           Feld[newx][newy] = EL_EMPTY;
8026           TEST_DrawLevelField(newx, newy);
8027         }
8028
8029         PlayLevelSound(x, y, SND_PIG_DIGGING);
8030       }
8031       else if (!IS_FREE(newx, newy))
8032       {
8033         if (IS_PLAYER(x, y))
8034           DrawPlayerField(x, y);
8035         else
8036           TEST_DrawLevelField(x, y);
8037
8038         return;
8039       }
8040     }
8041     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8042     {
8043       if (Store[x][y] != EL_EMPTY)
8044       {
8045         boolean can_clone = FALSE;
8046         int xx, yy;
8047
8048         // check if element to clone is still there
8049         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8050         {
8051           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8052           {
8053             can_clone = TRUE;
8054
8055             break;
8056           }
8057         }
8058
8059         // cannot clone or target field not free anymore -- do not clone
8060         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8061           Store[x][y] = EL_EMPTY;
8062       }
8063
8064       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8065       {
8066         if (IS_MV_DIAGONAL(MovDir[x][y]))
8067         {
8068           int diagonal_move_dir = MovDir[x][y];
8069           int stored = Store[x][y];
8070           int change_delay = 8;
8071           int graphic;
8072
8073           // android is moving diagonally
8074
8075           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8076
8077           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8078           GfxElement[x][y] = EL_EMC_ANDROID;
8079           GfxAction[x][y] = ACTION_SHRINKING;
8080           GfxDir[x][y] = diagonal_move_dir;
8081           ChangeDelay[x][y] = change_delay;
8082
8083           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8084                                    GfxDir[x][y]);
8085
8086           DrawLevelGraphicAnimation(x, y, graphic);
8087           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8088
8089           if (Feld[newx][newy] == EL_ACID)
8090           {
8091             SplashAcid(newx, newy);
8092
8093             return;
8094           }
8095
8096           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8097
8098           Store[newx][newy] = EL_EMC_ANDROID;
8099           GfxElement[newx][newy] = EL_EMC_ANDROID;
8100           GfxAction[newx][newy] = ACTION_GROWING;
8101           GfxDir[newx][newy] = diagonal_move_dir;
8102           ChangeDelay[newx][newy] = change_delay;
8103
8104           graphic = el_act_dir2img(GfxElement[newx][newy],
8105                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8106
8107           DrawLevelGraphicAnimation(newx, newy, graphic);
8108           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8109
8110           return;
8111         }
8112         else
8113         {
8114           Feld[newx][newy] = EL_EMPTY;
8115           TEST_DrawLevelField(newx, newy);
8116
8117           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8118         }
8119       }
8120       else if (!IS_FREE(newx, newy))
8121       {
8122         return;
8123       }
8124     }
8125     else if (IS_CUSTOM_ELEMENT(element) &&
8126              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8127     {
8128       if (!DigFieldByCE(newx, newy, element))
8129         return;
8130
8131       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8132       {
8133         RunnerVisit[x][y] = FrameCounter;
8134         PlayerVisit[x][y] /= 8;         // expire player visit path
8135       }
8136     }
8137     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8138     {
8139       if (!IS_FREE(newx, newy))
8140       {
8141         if (IS_PLAYER(x, y))
8142           DrawPlayerField(x, y);
8143         else
8144           TEST_DrawLevelField(x, y);
8145
8146         return;
8147       }
8148       else
8149       {
8150         boolean wanna_flame = !RND(10);
8151         int dx = newx - x, dy = newy - y;
8152         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8153         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8154         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8155                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8156         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8157                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8158
8159         if ((wanna_flame ||
8160              IS_CLASSIC_ENEMY(element1) ||
8161              IS_CLASSIC_ENEMY(element2)) &&
8162             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8163             element1 != EL_FLAMES && element2 != EL_FLAMES)
8164         {
8165           ResetGfxAnimation(x, y);
8166           GfxAction[x][y] = ACTION_ATTACKING;
8167
8168           if (IS_PLAYER(x, y))
8169             DrawPlayerField(x, y);
8170           else
8171             TEST_DrawLevelField(x, y);
8172
8173           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8174
8175           MovDelay[x][y] = 50;
8176
8177           Feld[newx][newy] = EL_FLAMES;
8178           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8179             Feld[newx1][newy1] = EL_FLAMES;
8180           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8181             Feld[newx2][newy2] = EL_FLAMES;
8182
8183           return;
8184         }
8185       }
8186     }
8187     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8188              Feld[newx][newy] == EL_DIAMOND)
8189     {
8190       if (IS_MOVING(newx, newy))
8191         RemoveMovingField(newx, newy);
8192       else
8193       {
8194         Feld[newx][newy] = EL_EMPTY;
8195         TEST_DrawLevelField(newx, newy);
8196       }
8197
8198       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8199     }
8200     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8201              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8202     {
8203       if (AmoebaNr[newx][newy])
8204       {
8205         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8206         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8207             Feld[newx][newy] == EL_BD_AMOEBA)
8208           AmoebaCnt[AmoebaNr[newx][newy]]--;
8209       }
8210
8211       if (IS_MOVING(newx, newy))
8212       {
8213         RemoveMovingField(newx, newy);
8214       }
8215       else
8216       {
8217         Feld[newx][newy] = EL_EMPTY;
8218         TEST_DrawLevelField(newx, newy);
8219       }
8220
8221       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8222     }
8223     else if ((element == EL_PACMAN || element == EL_MOLE)
8224              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8225     {
8226       if (AmoebaNr[newx][newy])
8227       {
8228         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8229         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8230             Feld[newx][newy] == EL_BD_AMOEBA)
8231           AmoebaCnt[AmoebaNr[newx][newy]]--;
8232       }
8233
8234       if (element == EL_MOLE)
8235       {
8236         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8237         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8238
8239         ResetGfxAnimation(x, y);
8240         GfxAction[x][y] = ACTION_DIGGING;
8241         TEST_DrawLevelField(x, y);
8242
8243         MovDelay[newx][newy] = 0;       // start amoeba shrinking delay
8244
8245         return;                         // wait for shrinking amoeba
8246       }
8247       else      // element == EL_PACMAN
8248       {
8249         Feld[newx][newy] = EL_EMPTY;
8250         TEST_DrawLevelField(newx, newy);
8251         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8252       }
8253     }
8254     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8255              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8256               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8257     {
8258       // wait for shrinking amoeba to completely disappear
8259       return;
8260     }
8261     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8262     {
8263       // object was running against a wall
8264
8265       TurnRound(x, y);
8266
8267       if (GFX_ELEMENT(element) != EL_SAND)     // !!! FIX THIS (crumble) !!!
8268         DrawLevelElementAnimation(x, y, element);
8269
8270       if (DONT_TOUCH(element))
8271         TestIfBadThingTouchesPlayer(x, y);
8272
8273       return;
8274     }
8275
8276     InitMovingField(x, y, MovDir[x][y]);
8277
8278     PlayLevelSoundAction(x, y, ACTION_MOVING);
8279   }
8280
8281   if (MovDir[x][y])
8282     ContinueMoving(x, y);
8283 }
8284
8285 void ContinueMoving(int x, int y)
8286 {
8287   int element = Feld[x][y];
8288   struct ElementInfo *ei = &element_info[element];
8289   int direction = MovDir[x][y];
8290   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8291   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8292   int newx = x + dx, newy = y + dy;
8293   int stored = Store[x][y];
8294   int stored_new = Store[newx][newy];
8295   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8296   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8297   boolean last_line = (newy == lev_fieldy - 1);
8298
8299   MovPos[x][y] += getElementMoveStepsize(x, y);
8300
8301   if (pushed_by_player) // special case: moving object pushed by player
8302     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8303
8304   if (ABS(MovPos[x][y]) < TILEX)
8305   {
8306     TEST_DrawLevelField(x, y);
8307
8308     return;     // element is still moving
8309   }
8310
8311   // element reached destination field
8312
8313   Feld[x][y] = EL_EMPTY;
8314   Feld[newx][newy] = element;
8315   MovPos[x][y] = 0;     // force "not moving" for "crumbled sand"
8316
8317   if (Store[x][y] == EL_ACID)   // element is moving into acid pool
8318   {
8319     element = Feld[newx][newy] = EL_ACID;
8320   }
8321   else if (element == EL_MOLE)
8322   {
8323     Feld[x][y] = EL_SAND;
8324
8325     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8326   }
8327   else if (element == EL_QUICKSAND_FILLING)
8328   {
8329     element = Feld[newx][newy] = get_next_element(element);
8330     Store[newx][newy] = Store[x][y];
8331   }
8332   else if (element == EL_QUICKSAND_EMPTYING)
8333   {
8334     Feld[x][y] = get_next_element(element);
8335     element = Feld[newx][newy] = Store[x][y];
8336   }
8337   else if (element == EL_QUICKSAND_FAST_FILLING)
8338   {
8339     element = Feld[newx][newy] = get_next_element(element);
8340     Store[newx][newy] = Store[x][y];
8341   }
8342   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8343   {
8344     Feld[x][y] = get_next_element(element);
8345     element = Feld[newx][newy] = Store[x][y];
8346   }
8347   else if (element == EL_MAGIC_WALL_FILLING)
8348   {
8349     element = Feld[newx][newy] = get_next_element(element);
8350     if (!game.magic_wall_active)
8351       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8352     Store[newx][newy] = Store[x][y];
8353   }
8354   else if (element == EL_MAGIC_WALL_EMPTYING)
8355   {
8356     Feld[x][y] = get_next_element(element);
8357     if (!game.magic_wall_active)
8358       Feld[x][y] = EL_MAGIC_WALL_DEAD;
8359     element = Feld[newx][newy] = Store[x][y];
8360
8361     InitField(newx, newy, FALSE);
8362   }
8363   else if (element == EL_BD_MAGIC_WALL_FILLING)
8364   {
8365     element = Feld[newx][newy] = get_next_element(element);
8366     if (!game.magic_wall_active)
8367       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8368     Store[newx][newy] = Store[x][y];
8369   }
8370   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8371   {
8372     Feld[x][y] = get_next_element(element);
8373     if (!game.magic_wall_active)
8374       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8375     element = Feld[newx][newy] = Store[x][y];
8376
8377     InitField(newx, newy, FALSE);
8378   }
8379   else if (element == EL_DC_MAGIC_WALL_FILLING)
8380   {
8381     element = Feld[newx][newy] = get_next_element(element);
8382     if (!game.magic_wall_active)
8383       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8384     Store[newx][newy] = Store[x][y];
8385   }
8386   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8387   {
8388     Feld[x][y] = get_next_element(element);
8389     if (!game.magic_wall_active)
8390       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8391     element = Feld[newx][newy] = Store[x][y];
8392
8393     InitField(newx, newy, FALSE);
8394   }
8395   else if (element == EL_AMOEBA_DROPPING)
8396   {
8397     Feld[x][y] = get_next_element(element);
8398     element = Feld[newx][newy] = Store[x][y];
8399   }
8400   else if (element == EL_SOKOBAN_OBJECT)
8401   {
8402     if (Back[x][y])
8403       Feld[x][y] = Back[x][y];
8404
8405     if (Back[newx][newy])
8406       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8407
8408     Back[x][y] = Back[newx][newy] = 0;
8409   }
8410
8411   Store[x][y] = EL_EMPTY;
8412   MovPos[x][y] = 0;
8413   MovDir[x][y] = 0;
8414   MovDelay[x][y] = 0;
8415
8416   MovDelay[newx][newy] = 0;
8417
8418   if (CAN_CHANGE_OR_HAS_ACTION(element))
8419   {
8420     // copy element change control values to new field
8421     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8422     ChangePage[newx][newy]  = ChangePage[x][y];
8423     ChangeCount[newx][newy] = ChangeCount[x][y];
8424     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8425   }
8426
8427   CustomValue[newx][newy] = CustomValue[x][y];
8428
8429   ChangeDelay[x][y] = 0;
8430   ChangePage[x][y] = -1;
8431   ChangeCount[x][y] = 0;
8432   ChangeEvent[x][y] = -1;
8433
8434   CustomValue[x][y] = 0;
8435
8436   // copy animation control values to new field
8437   GfxFrame[newx][newy]  = GfxFrame[x][y];
8438   GfxRandom[newx][newy] = GfxRandom[x][y];      // keep same random value
8439   GfxAction[newx][newy] = GfxAction[x][y];      // keep action one frame
8440   GfxDir[newx][newy]    = GfxDir[x][y];         // keep element direction
8441
8442   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8443
8444   // some elements can leave other elements behind after moving
8445   if (ei->move_leave_element != EL_EMPTY &&
8446       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8447       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8448   {
8449     int move_leave_element = ei->move_leave_element;
8450
8451     // this makes it possible to leave the removed element again
8452     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8453       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8454
8455     Feld[x][y] = move_leave_element;
8456
8457     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8458       MovDir[x][y] = direction;
8459
8460     InitField(x, y, FALSE);
8461
8462     if (GFX_CRUMBLED(Feld[x][y]))
8463       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8464
8465     if (ELEM_IS_PLAYER(move_leave_element))
8466       RelocatePlayer(x, y, move_leave_element);
8467   }
8468
8469   // do this after checking for left-behind element
8470   ResetGfxAnimation(x, y);      // reset animation values for old field
8471
8472   if (!CAN_MOVE(element) ||
8473       (CAN_FALL(element) && direction == MV_DOWN &&
8474        (element == EL_SPRING ||
8475         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8476         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8477     GfxDir[x][y] = MovDir[newx][newy] = 0;
8478
8479   TEST_DrawLevelField(x, y);
8480   TEST_DrawLevelField(newx, newy);
8481
8482   Stop[newx][newy] = TRUE;      // ignore this element until the next frame
8483
8484   // prevent pushed element from moving on in pushed direction
8485   if (pushed_by_player && CAN_MOVE(element) &&
8486       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8487       !(element_info[element].move_pattern & direction))
8488     TurnRound(newx, newy);
8489
8490   // prevent elements on conveyor belt from moving on in last direction
8491   if (pushed_by_conveyor && CAN_FALL(element) &&
8492       direction & MV_HORIZONTAL)
8493     MovDir[newx][newy] = 0;
8494
8495   if (!pushed_by_player)
8496   {
8497     int nextx = newx + dx, nexty = newy + dy;
8498     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8499
8500     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8501
8502     if (CAN_FALL(element) && direction == MV_DOWN)
8503       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8504
8505     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8506       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8507
8508     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8509       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8510   }
8511
8512   if (DONT_TOUCH(element))      // object may be nasty to player or others
8513   {
8514     TestIfBadThingTouchesPlayer(newx, newy);
8515     TestIfBadThingTouchesFriend(newx, newy);
8516
8517     if (!IS_CUSTOM_ELEMENT(element))
8518       TestIfBadThingTouchesOtherBadThing(newx, newy);
8519   }
8520   else if (element == EL_PENGUIN)
8521     TestIfFriendTouchesBadThing(newx, newy);
8522
8523   if (DONT_GET_HIT_BY(element))
8524   {
8525     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8526   }
8527
8528   // give the player one last chance (one more frame) to move away
8529   if (CAN_FALL(element) && direction == MV_DOWN &&
8530       (last_line || (!IS_FREE(x, newy + 1) &&
8531                      (!IS_PLAYER(x, newy + 1) ||
8532                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8533     Impact(x, newy);
8534
8535   if (pushed_by_player && !game.use_change_when_pushing_bug)
8536   {
8537     int push_side = MV_DIR_OPPOSITE(direction);
8538     struct PlayerInfo *player = PLAYERINFO(x, y);
8539
8540     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8541                                player->index_bit, push_side);
8542     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8543                                         player->index_bit, push_side);
8544   }
8545
8546   if (element == EL_EMC_ANDROID && pushed_by_player)    // make another move
8547     MovDelay[newx][newy] = 1;
8548
8549   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8550
8551   TestIfElementTouchesCustomElement(x, y);      // empty or new element
8552   TestIfElementHitsCustomElement(newx, newy, direction);
8553   TestIfPlayerTouchesCustomElement(newx, newy);
8554   TestIfElementTouchesCustomElement(newx, newy);
8555
8556   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8557       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8558     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8559                              MV_DIR_OPPOSITE(direction));
8560 }
8561
8562 int AmoebeNachbarNr(int ax, int ay)
8563 {
8564   int i;
8565   int element = Feld[ax][ay];
8566   int group_nr = 0;
8567   static int xy[4][2] =
8568   {
8569     { 0, -1 },
8570     { -1, 0 },
8571     { +1, 0 },
8572     { 0, +1 }
8573   };
8574
8575   for (i = 0; i < NUM_DIRECTIONS; i++)
8576   {
8577     int x = ax + xy[i][0];
8578     int y = ay + xy[i][1];
8579
8580     if (!IN_LEV_FIELD(x, y))
8581       continue;
8582
8583     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8584       group_nr = AmoebaNr[x][y];
8585   }
8586
8587   return group_nr;
8588 }
8589
8590 static void AmoebenVereinigen(int ax, int ay)
8591 {
8592   int i, x, y, xx, yy;
8593   int new_group_nr = AmoebaNr[ax][ay];
8594   static int xy[4][2] =
8595   {
8596     { 0, -1 },
8597     { -1, 0 },
8598     { +1, 0 },
8599     { 0, +1 }
8600   };
8601
8602   if (new_group_nr == 0)
8603     return;
8604
8605   for (i = 0; i < NUM_DIRECTIONS; i++)
8606   {
8607     x = ax + xy[i][0];
8608     y = ay + xy[i][1];
8609
8610     if (!IN_LEV_FIELD(x, y))
8611       continue;
8612
8613     if ((Feld[x][y] == EL_AMOEBA_FULL ||
8614          Feld[x][y] == EL_BD_AMOEBA ||
8615          Feld[x][y] == EL_AMOEBA_DEAD) &&
8616         AmoebaNr[x][y] != new_group_nr)
8617     {
8618       int old_group_nr = AmoebaNr[x][y];
8619
8620       if (old_group_nr == 0)
8621         return;
8622
8623       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8624       AmoebaCnt[old_group_nr] = 0;
8625       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8626       AmoebaCnt2[old_group_nr] = 0;
8627
8628       SCAN_PLAYFIELD(xx, yy)
8629       {
8630         if (AmoebaNr[xx][yy] == old_group_nr)
8631           AmoebaNr[xx][yy] = new_group_nr;
8632       }
8633     }
8634   }
8635 }
8636
8637 void AmoebeUmwandeln(int ax, int ay)
8638 {
8639   int i, x, y;
8640
8641   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8642   {
8643     int group_nr = AmoebaNr[ax][ay];
8644
8645 #ifdef DEBUG
8646     if (group_nr == 0)
8647     {
8648       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8649       printf("AmoebeUmwandeln(): This should never happen!\n");
8650       return;
8651     }
8652 #endif
8653
8654     SCAN_PLAYFIELD(x, y)
8655     {
8656       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8657       {
8658         AmoebaNr[x][y] = 0;
8659         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8660       }
8661     }
8662
8663     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8664                             SND_AMOEBA_TURNING_TO_GEM :
8665                             SND_AMOEBA_TURNING_TO_ROCK));
8666     Bang(ax, ay);
8667   }
8668   else
8669   {
8670     static int xy[4][2] =
8671     {
8672       { 0, -1 },
8673       { -1, 0 },
8674       { +1, 0 },
8675       { 0, +1 }
8676     };
8677
8678     for (i = 0; i < NUM_DIRECTIONS; i++)
8679     {
8680       x = ax + xy[i][0];
8681       y = ay + xy[i][1];
8682
8683       if (!IN_LEV_FIELD(x, y))
8684         continue;
8685
8686       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8687       {
8688         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8689                               SND_AMOEBA_TURNING_TO_GEM :
8690                               SND_AMOEBA_TURNING_TO_ROCK));
8691         Bang(x, y);
8692       }
8693     }
8694   }
8695 }
8696
8697 static void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8698 {
8699   int x, y;
8700   int group_nr = AmoebaNr[ax][ay];
8701   boolean done = FALSE;
8702
8703 #ifdef DEBUG
8704   if (group_nr == 0)
8705   {
8706     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8707     printf("AmoebeUmwandelnBD(): This should never happen!\n");
8708     return;
8709   }
8710 #endif
8711
8712   SCAN_PLAYFIELD(x, y)
8713   {
8714     if (AmoebaNr[x][y] == group_nr &&
8715         (Feld[x][y] == EL_AMOEBA_DEAD ||
8716          Feld[x][y] == EL_BD_AMOEBA ||
8717          Feld[x][y] == EL_AMOEBA_GROWING))
8718     {
8719       AmoebaNr[x][y] = 0;
8720       Feld[x][y] = new_element;
8721       InitField(x, y, FALSE);
8722       TEST_DrawLevelField(x, y);
8723       done = TRUE;
8724     }
8725   }
8726
8727   if (done)
8728     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8729                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8730                             SND_BD_AMOEBA_TURNING_TO_GEM));
8731 }
8732
8733 static void AmoebeWaechst(int x, int y)
8734 {
8735   static unsigned int sound_delay = 0;
8736   static unsigned int sound_delay_value = 0;
8737
8738   if (!MovDelay[x][y])          // start new growing cycle
8739   {
8740     MovDelay[x][y] = 7;
8741
8742     if (DelayReached(&sound_delay, sound_delay_value))
8743     {
8744       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8745       sound_delay_value = 30;
8746     }
8747   }
8748
8749   if (MovDelay[x][y])           // wait some time before growing bigger
8750   {
8751     MovDelay[x][y]--;
8752     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8753     {
8754       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8755                                            6 - MovDelay[x][y]);
8756
8757       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8758     }
8759
8760     if (!MovDelay[x][y])
8761     {
8762       Feld[x][y] = Store[x][y];
8763       Store[x][y] = 0;
8764       TEST_DrawLevelField(x, y);
8765     }
8766   }
8767 }
8768
8769 static void AmoebaDisappearing(int x, int y)
8770 {
8771   static unsigned int sound_delay = 0;
8772   static unsigned int sound_delay_value = 0;
8773
8774   if (!MovDelay[x][y])          // start new shrinking cycle
8775   {
8776     MovDelay[x][y] = 7;
8777
8778     if (DelayReached(&sound_delay, sound_delay_value))
8779       sound_delay_value = 30;
8780   }
8781
8782   if (MovDelay[x][y])           // wait some time before shrinking
8783   {
8784     MovDelay[x][y]--;
8785     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8786     {
8787       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8788                                            6 - MovDelay[x][y]);
8789
8790       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8791     }
8792
8793     if (!MovDelay[x][y])
8794     {
8795       Feld[x][y] = EL_EMPTY;
8796       TEST_DrawLevelField(x, y);
8797
8798       // don't let mole enter this field in this cycle;
8799       // (give priority to objects falling to this field from above)
8800       Stop[x][y] = TRUE;
8801     }
8802   }
8803 }
8804
8805 static void AmoebeAbleger(int ax, int ay)
8806 {
8807   int i;
8808   int element = Feld[ax][ay];
8809   int graphic = el2img(element);
8810   int newax = ax, neway = ay;
8811   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8812   static int xy[4][2] =
8813   {
8814     { 0, -1 },
8815     { -1, 0 },
8816     { +1, 0 },
8817     { 0, +1 }
8818   };
8819
8820   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8821   {
8822     Feld[ax][ay] = EL_AMOEBA_DEAD;
8823     TEST_DrawLevelField(ax, ay);
8824     return;
8825   }
8826
8827   if (IS_ANIMATED(graphic))
8828     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8829
8830   if (!MovDelay[ax][ay])        // start making new amoeba field
8831     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8832
8833   if (MovDelay[ax][ay])         // wait some time before making new amoeba
8834   {
8835     MovDelay[ax][ay]--;
8836     if (MovDelay[ax][ay])
8837       return;
8838   }
8839
8840   if (can_drop)                 // EL_AMOEBA_WET or EL_EMC_DRIPPER
8841   {
8842     int start = RND(4);
8843     int x = ax + xy[start][0];
8844     int y = ay + xy[start][1];
8845
8846     if (!IN_LEV_FIELD(x, y))
8847       return;
8848
8849     if (IS_FREE(x, y) ||
8850         CAN_GROW_INTO(Feld[x][y]) ||
8851         Feld[x][y] == EL_QUICKSAND_EMPTY ||
8852         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8853     {
8854       newax = x;
8855       neway = y;
8856     }
8857
8858     if (newax == ax && neway == ay)
8859       return;
8860   }
8861   else                          // normal or "filled" (BD style) amoeba
8862   {
8863     int start = RND(4);
8864     boolean waiting_for_player = FALSE;
8865
8866     for (i = 0; i < NUM_DIRECTIONS; i++)
8867     {
8868       int j = (start + i) % 4;
8869       int x = ax + xy[j][0];
8870       int y = ay + xy[j][1];
8871
8872       if (!IN_LEV_FIELD(x, y))
8873         continue;
8874
8875       if (IS_FREE(x, y) ||
8876           CAN_GROW_INTO(Feld[x][y]) ||
8877           Feld[x][y] == EL_QUICKSAND_EMPTY ||
8878           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8879       {
8880         newax = x;
8881         neway = y;
8882         break;
8883       }
8884       else if (IS_PLAYER(x, y))
8885         waiting_for_player = TRUE;
8886     }
8887
8888     if (newax == ax && neway == ay)             // amoeba cannot grow
8889     {
8890       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8891       {
8892         Feld[ax][ay] = EL_AMOEBA_DEAD;
8893         TEST_DrawLevelField(ax, ay);
8894         AmoebaCnt[AmoebaNr[ax][ay]]--;
8895
8896         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   // amoeba is completely dead
8897         {
8898           if (element == EL_AMOEBA_FULL)
8899             AmoebeUmwandeln(ax, ay);
8900           else if (element == EL_BD_AMOEBA)
8901             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8902         }
8903       }
8904       return;
8905     }
8906     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8907     {
8908       // amoeba gets larger by growing in some direction
8909
8910       int new_group_nr = AmoebaNr[ax][ay];
8911
8912 #ifdef DEBUG
8913   if (new_group_nr == 0)
8914   {
8915     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8916     printf("AmoebeAbleger(): This should never happen!\n");
8917     return;
8918   }
8919 #endif
8920
8921       AmoebaNr[newax][neway] = new_group_nr;
8922       AmoebaCnt[new_group_nr]++;
8923       AmoebaCnt2[new_group_nr]++;
8924
8925       // if amoeba touches other amoeba(s) after growing, unify them
8926       AmoebenVereinigen(newax, neway);
8927
8928       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8929       {
8930         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8931         return;
8932       }
8933     }
8934   }
8935
8936   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8937       (neway == lev_fieldy - 1 && newax != ax))
8938   {
8939     Feld[newax][neway] = EL_AMOEBA_GROWING;     // creation of new amoeba
8940     Store[newax][neway] = element;
8941   }
8942   else if (neway == ay || element == EL_EMC_DRIPPER)
8943   {
8944     Feld[newax][neway] = EL_AMOEBA_DROP;        // drop left/right of amoeba
8945
8946     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8947   }
8948   else
8949   {
8950     InitMovingField(ax, ay, MV_DOWN);           // drop dripping from amoeba
8951     Feld[ax][ay] = EL_AMOEBA_DROPPING;
8952     Store[ax][ay] = EL_AMOEBA_DROP;
8953     ContinueMoving(ax, ay);
8954     return;
8955   }
8956
8957   TEST_DrawLevelField(newax, neway);
8958 }
8959
8960 static void Life(int ax, int ay)
8961 {
8962   int x1, y1, x2, y2;
8963   int life_time = 40;
8964   int element = Feld[ax][ay];
8965   int graphic = el2img(element);
8966   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8967                          level.biomaze);
8968   boolean changed = FALSE;
8969
8970   if (IS_ANIMATED(graphic))
8971     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8972
8973   if (Stop[ax][ay])
8974     return;
8975
8976   if (!MovDelay[ax][ay])        // start new "game of life" cycle
8977     MovDelay[ax][ay] = life_time;
8978
8979   if (MovDelay[ax][ay])         // wait some time before next cycle
8980   {
8981     MovDelay[ax][ay]--;
8982     if (MovDelay[ax][ay])
8983       return;
8984   }
8985
8986   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8987   {
8988     int xx = ax+x1, yy = ay+y1;
8989     int old_element = Feld[xx][yy];
8990     int num_neighbours = 0;
8991
8992     if (!IN_LEV_FIELD(xx, yy))
8993       continue;
8994
8995     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8996     {
8997       int x = xx+x2, y = yy+y2;
8998
8999       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9000         continue;
9001
9002       boolean is_player_cell = (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y));
9003       boolean is_neighbour = FALSE;
9004
9005       if (level.use_life_bugs)
9006         is_neighbour =
9007           (((Feld[x][y] == element || is_player_cell) && !Stop[x][y]) ||
9008            (IS_FREE(x, y)                             &&  Stop[x][y]));
9009       else
9010         is_neighbour =
9011           (Last[x][y] == element || is_player_cell);
9012
9013       if (is_neighbour)
9014         num_neighbours++;
9015     }
9016
9017     boolean is_free = FALSE;
9018
9019     if (level.use_life_bugs)
9020       is_free = (IS_FREE(xx, yy));
9021     else
9022       is_free = (IS_FREE(xx, yy) && Last[xx][yy] == EL_EMPTY);
9023
9024     if (xx == ax && yy == ay)           // field in the middle
9025     {
9026       if (num_neighbours < life_parameter[0] ||
9027           num_neighbours > life_parameter[1])
9028       {
9029         Feld[xx][yy] = EL_EMPTY;
9030         if (Feld[xx][yy] != old_element)
9031           TEST_DrawLevelField(xx, yy);
9032         Stop[xx][yy] = TRUE;
9033         changed = TRUE;
9034       }
9035     }
9036     else if (is_free || CAN_GROW_INTO(Feld[xx][yy]))
9037     {                                   // free border field
9038       if (num_neighbours >= life_parameter[2] &&
9039           num_neighbours <= life_parameter[3])
9040       {
9041         Feld[xx][yy] = element;
9042         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9043         if (Feld[xx][yy] != old_element)
9044           TEST_DrawLevelField(xx, yy);
9045         Stop[xx][yy] = TRUE;
9046         changed = TRUE;
9047       }
9048     }
9049   }
9050
9051   if (changed)
9052     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9053                    SND_GAME_OF_LIFE_GROWING);
9054 }
9055
9056 static void InitRobotWheel(int x, int y)
9057 {
9058   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9059 }
9060
9061 static void RunRobotWheel(int x, int y)
9062 {
9063   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9064 }
9065
9066 static void StopRobotWheel(int x, int y)
9067 {
9068   if (game.robot_wheel_x == x &&
9069       game.robot_wheel_y == y)
9070   {
9071     game.robot_wheel_x = -1;
9072     game.robot_wheel_y = -1;
9073     game.robot_wheel_active = FALSE;
9074   }
9075 }
9076
9077 static void InitTimegateWheel(int x, int y)
9078 {
9079   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9080 }
9081
9082 static void RunTimegateWheel(int x, int y)
9083 {
9084   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9085 }
9086
9087 static void InitMagicBallDelay(int x, int y)
9088 {
9089   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9090 }
9091
9092 static void ActivateMagicBall(int bx, int by)
9093 {
9094   int x, y;
9095
9096   if (level.ball_random)
9097   {
9098     int pos_border = RND(8);    // select one of the eight border elements
9099     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9100     int xx = pos_content % 3;
9101     int yy = pos_content / 3;
9102
9103     x = bx - 1 + xx;
9104     y = by - 1 + yy;
9105
9106     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9107       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9108   }
9109   else
9110   {
9111     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9112     {
9113       int xx = x - bx + 1;
9114       int yy = y - by + 1;
9115
9116       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9117         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9118     }
9119   }
9120
9121   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9122 }
9123
9124 static void CheckExit(int x, int y)
9125 {
9126   if (game.gems_still_needed > 0 ||
9127       game.sokoban_fields_still_needed > 0 ||
9128       game.sokoban_objects_still_needed > 0 ||
9129       game.lights_still_needed > 0)
9130   {
9131     int element = Feld[x][y];
9132     int graphic = el2img(element);
9133
9134     if (IS_ANIMATED(graphic))
9135       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9136
9137     return;
9138   }
9139
9140   // do not re-open exit door closed after last player
9141   if (game.all_players_gone)
9142     return;
9143
9144   Feld[x][y] = EL_EXIT_OPENING;
9145
9146   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9147 }
9148
9149 static void CheckExitEM(int x, int y)
9150 {
9151   if (game.gems_still_needed > 0 ||
9152       game.sokoban_fields_still_needed > 0 ||
9153       game.sokoban_objects_still_needed > 0 ||
9154       game.lights_still_needed > 0)
9155   {
9156     int element = Feld[x][y];
9157     int graphic = el2img(element);
9158
9159     if (IS_ANIMATED(graphic))
9160       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9161
9162     return;
9163   }
9164
9165   // do not re-open exit door closed after last player
9166   if (game.all_players_gone)
9167     return;
9168
9169   Feld[x][y] = EL_EM_EXIT_OPENING;
9170
9171   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9172 }
9173
9174 static void CheckExitSteel(int x, int y)
9175 {
9176   if (game.gems_still_needed > 0 ||
9177       game.sokoban_fields_still_needed > 0 ||
9178       game.sokoban_objects_still_needed > 0 ||
9179       game.lights_still_needed > 0)
9180   {
9181     int element = Feld[x][y];
9182     int graphic = el2img(element);
9183
9184     if (IS_ANIMATED(graphic))
9185       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9186
9187     return;
9188   }
9189
9190   // do not re-open exit door closed after last player
9191   if (game.all_players_gone)
9192     return;
9193
9194   Feld[x][y] = EL_STEEL_EXIT_OPENING;
9195
9196   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9197 }
9198
9199 static void CheckExitSteelEM(int x, int y)
9200 {
9201   if (game.gems_still_needed > 0 ||
9202       game.sokoban_fields_still_needed > 0 ||
9203       game.sokoban_objects_still_needed > 0 ||
9204       game.lights_still_needed > 0)
9205   {
9206     int element = Feld[x][y];
9207     int graphic = el2img(element);
9208
9209     if (IS_ANIMATED(graphic))
9210       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9211
9212     return;
9213   }
9214
9215   // do not re-open exit door closed after last player
9216   if (game.all_players_gone)
9217     return;
9218
9219   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9220
9221   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9222 }
9223
9224 static void CheckExitSP(int x, int y)
9225 {
9226   if (game.gems_still_needed > 0)
9227   {
9228     int element = Feld[x][y];
9229     int graphic = el2img(element);
9230
9231     if (IS_ANIMATED(graphic))
9232       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9233
9234     return;
9235   }
9236
9237   // do not re-open exit door closed after last player
9238   if (game.all_players_gone)
9239     return;
9240
9241   Feld[x][y] = EL_SP_EXIT_OPENING;
9242
9243   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9244 }
9245
9246 static void CloseAllOpenTimegates(void)
9247 {
9248   int x, y;
9249
9250   SCAN_PLAYFIELD(x, y)
9251   {
9252     int element = Feld[x][y];
9253
9254     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9255     {
9256       Feld[x][y] = EL_TIMEGATE_CLOSING;
9257
9258       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9259     }
9260   }
9261 }
9262
9263 static void DrawTwinkleOnField(int x, int y)
9264 {
9265   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9266     return;
9267
9268   if (Feld[x][y] == EL_BD_DIAMOND)
9269     return;
9270
9271   if (MovDelay[x][y] == 0)      // next animation frame
9272     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9273
9274   if (MovDelay[x][y] != 0)      // wait some time before next frame
9275   {
9276     MovDelay[x][y]--;
9277
9278     DrawLevelElementAnimation(x, y, Feld[x][y]);
9279
9280     if (MovDelay[x][y] != 0)
9281     {
9282       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9283                                            10 - MovDelay[x][y]);
9284
9285       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9286     }
9287   }
9288 }
9289
9290 static void MauerWaechst(int x, int y)
9291 {
9292   int delay = 6;
9293
9294   if (!MovDelay[x][y])          // next animation frame
9295     MovDelay[x][y] = 3 * delay;
9296
9297   if (MovDelay[x][y])           // wait some time before next frame
9298   {
9299     MovDelay[x][y]--;
9300
9301     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9302     {
9303       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9304       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9305
9306       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9307     }
9308
9309     if (!MovDelay[x][y])
9310     {
9311       if (MovDir[x][y] == MV_LEFT)
9312       {
9313         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9314           TEST_DrawLevelField(x - 1, y);
9315       }
9316       else if (MovDir[x][y] == MV_RIGHT)
9317       {
9318         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9319           TEST_DrawLevelField(x + 1, y);
9320       }
9321       else if (MovDir[x][y] == MV_UP)
9322       {
9323         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9324           TEST_DrawLevelField(x, y - 1);
9325       }
9326       else
9327       {
9328         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9329           TEST_DrawLevelField(x, y + 1);
9330       }
9331
9332       Feld[x][y] = Store[x][y];
9333       Store[x][y] = 0;
9334       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9335       TEST_DrawLevelField(x, y);
9336     }
9337   }
9338 }
9339
9340 static void MauerAbleger(int ax, int ay)
9341 {
9342   int element = Feld[ax][ay];
9343   int graphic = el2img(element);
9344   boolean oben_frei = FALSE, unten_frei = FALSE;
9345   boolean links_frei = FALSE, rechts_frei = FALSE;
9346   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9347   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9348   boolean new_wall = FALSE;
9349
9350   if (IS_ANIMATED(graphic))
9351     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9352
9353   if (!MovDelay[ax][ay])        // start building new wall
9354     MovDelay[ax][ay] = 6;
9355
9356   if (MovDelay[ax][ay])         // wait some time before building new wall
9357   {
9358     MovDelay[ax][ay]--;
9359     if (MovDelay[ax][ay])
9360       return;
9361   }
9362
9363   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9364     oben_frei = TRUE;
9365   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9366     unten_frei = TRUE;
9367   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9368     links_frei = TRUE;
9369   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9370     rechts_frei = TRUE;
9371
9372   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9373       element == EL_EXPANDABLE_WALL_ANY)
9374   {
9375     if (oben_frei)
9376     {
9377       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9378       Store[ax][ay-1] = element;
9379       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9380       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9381         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9382                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9383       new_wall = TRUE;
9384     }
9385     if (unten_frei)
9386     {
9387       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9388       Store[ax][ay+1] = element;
9389       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9390       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9391         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9392                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9393       new_wall = TRUE;
9394     }
9395   }
9396
9397   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9398       element == EL_EXPANDABLE_WALL_ANY ||
9399       element == EL_EXPANDABLE_WALL ||
9400       element == EL_BD_EXPANDABLE_WALL)
9401   {
9402     if (links_frei)
9403     {
9404       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9405       Store[ax-1][ay] = element;
9406       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9407       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9408         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9409                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9410       new_wall = TRUE;
9411     }
9412
9413     if (rechts_frei)
9414     {
9415       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9416       Store[ax+1][ay] = element;
9417       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9418       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9419         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9420                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9421       new_wall = TRUE;
9422     }
9423   }
9424
9425   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9426     TEST_DrawLevelField(ax, ay);
9427
9428   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9429     oben_massiv = TRUE;
9430   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9431     unten_massiv = TRUE;
9432   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9433     links_massiv = TRUE;
9434   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9435     rechts_massiv = TRUE;
9436
9437   if (((oben_massiv && unten_massiv) ||
9438        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9439        element == EL_EXPANDABLE_WALL) &&
9440       ((links_massiv && rechts_massiv) ||
9441        element == EL_EXPANDABLE_WALL_VERTICAL))
9442     Feld[ax][ay] = EL_WALL;
9443
9444   if (new_wall)
9445     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9446 }
9447
9448 static void MauerAblegerStahl(int ax, int ay)
9449 {
9450   int element = Feld[ax][ay];
9451   int graphic = el2img(element);
9452   boolean oben_frei = FALSE, unten_frei = FALSE;
9453   boolean links_frei = FALSE, rechts_frei = FALSE;
9454   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9455   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9456   boolean new_wall = FALSE;
9457
9458   if (IS_ANIMATED(graphic))
9459     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9460
9461   if (!MovDelay[ax][ay])        // start building new wall
9462     MovDelay[ax][ay] = 6;
9463
9464   if (MovDelay[ax][ay])         // wait some time before building new wall
9465   {
9466     MovDelay[ax][ay]--;
9467     if (MovDelay[ax][ay])
9468       return;
9469   }
9470
9471   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9472     oben_frei = TRUE;
9473   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9474     unten_frei = TRUE;
9475   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9476     links_frei = TRUE;
9477   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9478     rechts_frei = TRUE;
9479
9480   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9481       element == EL_EXPANDABLE_STEELWALL_ANY)
9482   {
9483     if (oben_frei)
9484     {
9485       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9486       Store[ax][ay-1] = element;
9487       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9488       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9489         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9490                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9491       new_wall = TRUE;
9492     }
9493     if (unten_frei)
9494     {
9495       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9496       Store[ax][ay+1] = element;
9497       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9498       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9499         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9500                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9501       new_wall = TRUE;
9502     }
9503   }
9504
9505   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9506       element == EL_EXPANDABLE_STEELWALL_ANY)
9507   {
9508     if (links_frei)
9509     {
9510       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9511       Store[ax-1][ay] = element;
9512       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9513       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9514         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9515                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9516       new_wall = TRUE;
9517     }
9518
9519     if (rechts_frei)
9520     {
9521       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9522       Store[ax+1][ay] = element;
9523       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9524       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9525         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9526                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9527       new_wall = TRUE;
9528     }
9529   }
9530
9531   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9532     oben_massiv = TRUE;
9533   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9534     unten_massiv = TRUE;
9535   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9536     links_massiv = TRUE;
9537   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9538     rechts_massiv = TRUE;
9539
9540   if (((oben_massiv && unten_massiv) ||
9541        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9542       ((links_massiv && rechts_massiv) ||
9543        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9544     Feld[ax][ay] = EL_STEELWALL;
9545
9546   if (new_wall)
9547     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9548 }
9549
9550 static void CheckForDragon(int x, int y)
9551 {
9552   int i, j;
9553   boolean dragon_found = FALSE;
9554   static int xy[4][2] =
9555   {
9556     { 0, -1 },
9557     { -1, 0 },
9558     { +1, 0 },
9559     { 0, +1 }
9560   };
9561
9562   for (i = 0; i < NUM_DIRECTIONS; i++)
9563   {
9564     for (j = 0; j < 4; j++)
9565     {
9566       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9567
9568       if (IN_LEV_FIELD(xx, yy) &&
9569           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9570       {
9571         if (Feld[xx][yy] == EL_DRAGON)
9572           dragon_found = TRUE;
9573       }
9574       else
9575         break;
9576     }
9577   }
9578
9579   if (!dragon_found)
9580   {
9581     for (i = 0; i < NUM_DIRECTIONS; i++)
9582     {
9583       for (j = 0; j < 3; j++)
9584       {
9585         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9586   
9587         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9588         {
9589           Feld[xx][yy] = EL_EMPTY;
9590           TEST_DrawLevelField(xx, yy);
9591         }
9592         else
9593           break;
9594       }
9595     }
9596   }
9597 }
9598
9599 static void InitBuggyBase(int x, int y)
9600 {
9601   int element = Feld[x][y];
9602   int activating_delay = FRAMES_PER_SECOND / 4;
9603
9604   ChangeDelay[x][y] =
9605     (element == EL_SP_BUGGY_BASE ?
9606      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9607      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9608      activating_delay :
9609      element == EL_SP_BUGGY_BASE_ACTIVE ?
9610      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9611 }
9612
9613 static void WarnBuggyBase(int x, int y)
9614 {
9615   int i;
9616   static int xy[4][2] =
9617   {
9618     { 0, -1 },
9619     { -1, 0 },
9620     { +1, 0 },
9621     { 0, +1 }
9622   };
9623
9624   for (i = 0; i < NUM_DIRECTIONS; i++)
9625   {
9626     int xx = x + xy[i][0];
9627     int yy = y + xy[i][1];
9628
9629     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9630     {
9631       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9632
9633       break;
9634     }
9635   }
9636 }
9637
9638 static void InitTrap(int x, int y)
9639 {
9640   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9641 }
9642
9643 static void ActivateTrap(int x, int y)
9644 {
9645   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9646 }
9647
9648 static void ChangeActiveTrap(int x, int y)
9649 {
9650   int graphic = IMG_TRAP_ACTIVE;
9651
9652   // if new animation frame was drawn, correct crumbled sand border
9653   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9654     TEST_DrawLevelFieldCrumbled(x, y);
9655 }
9656
9657 static int getSpecialActionElement(int element, int number, int base_element)
9658 {
9659   return (element != EL_EMPTY ? element :
9660           number != -1 ? base_element + number - 1 :
9661           EL_EMPTY);
9662 }
9663
9664 static int getModifiedActionNumber(int value_old, int operator, int operand,
9665                                    int value_min, int value_max)
9666 {
9667   int value_new = (operator == CA_MODE_SET      ? operand :
9668                    operator == CA_MODE_ADD      ? value_old + operand :
9669                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9670                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9671                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9672                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9673                    value_old);
9674
9675   return (value_new < value_min ? value_min :
9676           value_new > value_max ? value_max :
9677           value_new);
9678 }
9679
9680 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9681 {
9682   struct ElementInfo *ei = &element_info[element];
9683   struct ElementChangeInfo *change = &ei->change_page[page];
9684   int target_element = change->target_element;
9685   int action_type = change->action_type;
9686   int action_mode = change->action_mode;
9687   int action_arg = change->action_arg;
9688   int action_element = change->action_element;
9689   int i;
9690
9691   if (!change->has_action)
9692     return;
9693
9694   // ---------- determine action paramater values -----------------------------
9695
9696   int level_time_value =
9697     (level.time > 0 ? TimeLeft :
9698      TimePlayed);
9699
9700   int action_arg_element_raw =
9701     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9702      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9703      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9704      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
9705      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9706      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
9707      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
9708      EL_EMPTY);
9709   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9710
9711   int action_arg_direction =
9712     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9713      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9714      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9715      change->actual_trigger_side :
9716      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9717      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9718      MV_NONE);
9719
9720   int action_arg_number_min =
9721     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9722      CA_ARG_MIN);
9723
9724   int action_arg_number_max =
9725     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9726      action_type == CA_SET_LEVEL_GEMS ? 999 :
9727      action_type == CA_SET_LEVEL_TIME ? 9999 :
9728      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9729      action_type == CA_SET_CE_VALUE ? 9999 :
9730      action_type == CA_SET_CE_SCORE ? 9999 :
9731      CA_ARG_MAX);
9732
9733   int action_arg_number_reset =
9734     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9735      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9736      action_type == CA_SET_LEVEL_TIME ? level.time :
9737      action_type == CA_SET_LEVEL_SCORE ? 0 :
9738      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9739      action_type == CA_SET_CE_SCORE ? 0 :
9740      0);
9741
9742   int action_arg_number =
9743     (action_arg <= CA_ARG_MAX ? action_arg :
9744      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9745      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9746      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9747      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9748      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9749      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9750      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9751      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9752      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9753      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9754      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? game.gems_still_needed :
9755      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? game.score :
9756      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9757      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9758      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9759      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9760      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9761      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9762      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
9763      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9764      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
9765      -1);
9766
9767   int action_arg_number_old =
9768     (action_type == CA_SET_LEVEL_GEMS ? game.gems_still_needed :
9769      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9770      action_type == CA_SET_LEVEL_SCORE ? game.score :
9771      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9772      action_type == CA_SET_CE_SCORE ? ei->collect_score :
9773      0);
9774
9775   int action_arg_number_new =
9776     getModifiedActionNumber(action_arg_number_old,
9777                             action_mode, action_arg_number,
9778                             action_arg_number_min, action_arg_number_max);
9779
9780   int trigger_player_bits =
9781     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9782      change->actual_trigger_player_bits : change->trigger_player);
9783
9784   int action_arg_player_bits =
9785     (action_arg >= CA_ARG_PLAYER_1 &&
9786      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9787      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9788      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9789      PLAYER_BITS_ANY);
9790
9791   // ---------- execute action  -----------------------------------------------
9792
9793   switch (action_type)
9794   {
9795     case CA_NO_ACTION:
9796     {
9797       return;
9798     }
9799
9800     // ---------- level actions  ----------------------------------------------
9801
9802     case CA_RESTART_LEVEL:
9803     {
9804       game.restart_level = TRUE;
9805
9806       break;
9807     }
9808
9809     case CA_SHOW_ENVELOPE:
9810     {
9811       int element = getSpecialActionElement(action_arg_element,
9812                                             action_arg_number, EL_ENVELOPE_1);
9813
9814       if (IS_ENVELOPE(element))
9815         local_player->show_envelope = element;
9816
9817       break;
9818     }
9819
9820     case CA_SET_LEVEL_TIME:
9821     {
9822       if (level.time > 0)       // only modify limited time value
9823       {
9824         TimeLeft = action_arg_number_new;
9825
9826         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9827
9828         DisplayGameControlValues();
9829
9830         if (!TimeLeft && setup.time_limit)
9831           for (i = 0; i < MAX_PLAYERS; i++)
9832             KillPlayer(&stored_player[i]);
9833       }
9834
9835       break;
9836     }
9837
9838     case CA_SET_LEVEL_SCORE:
9839     {
9840       game.score = action_arg_number_new;
9841
9842       game_panel_controls[GAME_PANEL_SCORE].value = game.score;
9843
9844       DisplayGameControlValues();
9845
9846       break;
9847     }
9848
9849     case CA_SET_LEVEL_GEMS:
9850     {
9851       game.gems_still_needed = action_arg_number_new;
9852
9853       game.snapshot.collected_item = TRUE;
9854
9855       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
9856
9857       DisplayGameControlValues();
9858
9859       break;
9860     }
9861
9862     case CA_SET_LEVEL_WIND:
9863     {
9864       game.wind_direction = action_arg_direction;
9865
9866       break;
9867     }
9868
9869     case CA_SET_LEVEL_RANDOM_SEED:
9870     {
9871       // ensure that setting a new random seed while playing is predictable
9872       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
9873
9874       break;
9875     }
9876
9877     // ---------- player actions  ---------------------------------------------
9878
9879     case CA_MOVE_PLAYER:
9880     {
9881       // automatically move to the next field in specified direction
9882       for (i = 0; i < MAX_PLAYERS; i++)
9883         if (trigger_player_bits & (1 << i))
9884           stored_player[i].programmed_action = action_arg_direction;
9885
9886       break;
9887     }
9888
9889     case CA_EXIT_PLAYER:
9890     {
9891       for (i = 0; i < MAX_PLAYERS; i++)
9892         if (action_arg_player_bits & (1 << i))
9893           ExitPlayer(&stored_player[i]);
9894
9895       if (game.players_still_needed == 0)
9896         LevelSolved();
9897
9898       break;
9899     }
9900
9901     case CA_KILL_PLAYER:
9902     {
9903       for (i = 0; i < MAX_PLAYERS; i++)
9904         if (action_arg_player_bits & (1 << i))
9905           KillPlayer(&stored_player[i]);
9906
9907       break;
9908     }
9909
9910     case CA_SET_PLAYER_KEYS:
9911     {
9912       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9913       int element = getSpecialActionElement(action_arg_element,
9914                                             action_arg_number, EL_KEY_1);
9915
9916       if (IS_KEY(element))
9917       {
9918         for (i = 0; i < MAX_PLAYERS; i++)
9919         {
9920           if (trigger_player_bits & (1 << i))
9921           {
9922             stored_player[i].key[KEY_NR(element)] = key_state;
9923
9924             DrawGameDoorValues();
9925           }
9926         }
9927       }
9928
9929       break;
9930     }
9931
9932     case CA_SET_PLAYER_SPEED:
9933     {
9934       for (i = 0; i < MAX_PLAYERS; i++)
9935       {
9936         if (trigger_player_bits & (1 << i))
9937         {
9938           int move_stepsize = TILEX / stored_player[i].move_delay_value;
9939
9940           if (action_arg == CA_ARG_SPEED_FASTER &&
9941               stored_player[i].cannot_move)
9942           {
9943             action_arg_number = STEPSIZE_VERY_SLOW;
9944           }
9945           else if (action_arg == CA_ARG_SPEED_SLOWER ||
9946                    action_arg == CA_ARG_SPEED_FASTER)
9947           {
9948             action_arg_number = 2;
9949             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9950                            CA_MODE_MULTIPLY);
9951           }
9952           else if (action_arg == CA_ARG_NUMBER_RESET)
9953           {
9954             action_arg_number = level.initial_player_stepsize[i];
9955           }
9956
9957           move_stepsize =
9958             getModifiedActionNumber(move_stepsize,
9959                                     action_mode,
9960                                     action_arg_number,
9961                                     action_arg_number_min,
9962                                     action_arg_number_max);
9963
9964           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9965         }
9966       }
9967
9968       break;
9969     }
9970
9971     case CA_SET_PLAYER_SHIELD:
9972     {
9973       for (i = 0; i < MAX_PLAYERS; i++)
9974       {
9975         if (trigger_player_bits & (1 << i))
9976         {
9977           if (action_arg == CA_ARG_SHIELD_OFF)
9978           {
9979             stored_player[i].shield_normal_time_left = 0;
9980             stored_player[i].shield_deadly_time_left = 0;
9981           }
9982           else if (action_arg == CA_ARG_SHIELD_NORMAL)
9983           {
9984             stored_player[i].shield_normal_time_left = 999999;
9985           }
9986           else if (action_arg == CA_ARG_SHIELD_DEADLY)
9987           {
9988             stored_player[i].shield_normal_time_left = 999999;
9989             stored_player[i].shield_deadly_time_left = 999999;
9990           }
9991         }
9992       }
9993
9994       break;
9995     }
9996
9997     case CA_SET_PLAYER_GRAVITY:
9998     {
9999       for (i = 0; i < MAX_PLAYERS; i++)
10000       {
10001         if (trigger_player_bits & (1 << i))
10002         {
10003           stored_player[i].gravity =
10004             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10005              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10006              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10007              stored_player[i].gravity);
10008         }
10009       }
10010
10011       break;
10012     }
10013
10014     case CA_SET_PLAYER_ARTWORK:
10015     {
10016       for (i = 0; i < MAX_PLAYERS; i++)
10017       {
10018         if (trigger_player_bits & (1 << i))
10019         {
10020           int artwork_element = action_arg_element;
10021
10022           if (action_arg == CA_ARG_ELEMENT_RESET)
10023             artwork_element =
10024               (level.use_artwork_element[i] ? level.artwork_element[i] :
10025                stored_player[i].element_nr);
10026
10027           if (stored_player[i].artwork_element != artwork_element)
10028             stored_player[i].Frame = 0;
10029
10030           stored_player[i].artwork_element = artwork_element;
10031
10032           SetPlayerWaiting(&stored_player[i], FALSE);
10033
10034           // set number of special actions for bored and sleeping animation
10035           stored_player[i].num_special_action_bored =
10036             get_num_special_action(artwork_element,
10037                                    ACTION_BORING_1, ACTION_BORING_LAST);
10038           stored_player[i].num_special_action_sleeping =
10039             get_num_special_action(artwork_element,
10040                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10041         }
10042       }
10043
10044       break;
10045     }
10046
10047     case CA_SET_PLAYER_INVENTORY:
10048     {
10049       for (i = 0; i < MAX_PLAYERS; i++)
10050       {
10051         struct PlayerInfo *player = &stored_player[i];
10052         int j, k;
10053
10054         if (trigger_player_bits & (1 << i))
10055         {
10056           int inventory_element = action_arg_element;
10057
10058           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10059               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10060               action_arg == CA_ARG_ELEMENT_ACTION)
10061           {
10062             int element = inventory_element;
10063             int collect_count = element_info[element].collect_count_initial;
10064
10065             if (!IS_CUSTOM_ELEMENT(element))
10066               collect_count = 1;
10067
10068             if (collect_count == 0)
10069               player->inventory_infinite_element = element;
10070             else
10071               for (k = 0; k < collect_count; k++)
10072                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10073                   player->inventory_element[player->inventory_size++] =
10074                     element;
10075           }
10076           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10077                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10078                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10079           {
10080             if (player->inventory_infinite_element != EL_UNDEFINED &&
10081                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10082                                      action_arg_element_raw))
10083               player->inventory_infinite_element = EL_UNDEFINED;
10084
10085             for (k = 0, j = 0; j < player->inventory_size; j++)
10086             {
10087               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10088                                         action_arg_element_raw))
10089                 player->inventory_element[k++] = player->inventory_element[j];
10090             }
10091
10092             player->inventory_size = k;
10093           }
10094           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10095           {
10096             if (player->inventory_size > 0)
10097             {
10098               for (j = 0; j < player->inventory_size - 1; j++)
10099                 player->inventory_element[j] = player->inventory_element[j + 1];
10100
10101               player->inventory_size--;
10102             }
10103           }
10104           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10105           {
10106             if (player->inventory_size > 0)
10107               player->inventory_size--;
10108           }
10109           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10110           {
10111             player->inventory_infinite_element = EL_UNDEFINED;
10112             player->inventory_size = 0;
10113           }
10114           else if (action_arg == CA_ARG_INVENTORY_RESET)
10115           {
10116             player->inventory_infinite_element = EL_UNDEFINED;
10117             player->inventory_size = 0;
10118
10119             if (level.use_initial_inventory[i])
10120             {
10121               for (j = 0; j < level.initial_inventory_size[i]; j++)
10122               {
10123                 int element = level.initial_inventory_content[i][j];
10124                 int collect_count = element_info[element].collect_count_initial;
10125
10126                 if (!IS_CUSTOM_ELEMENT(element))
10127                   collect_count = 1;
10128
10129                 if (collect_count == 0)
10130                   player->inventory_infinite_element = element;
10131                 else
10132                   for (k = 0; k < collect_count; k++)
10133                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10134                       player->inventory_element[player->inventory_size++] =
10135                         element;
10136               }
10137             }
10138           }
10139         }
10140       }
10141
10142       break;
10143     }
10144
10145     // ---------- CE actions  -------------------------------------------------
10146
10147     case CA_SET_CE_VALUE:
10148     {
10149       int last_ce_value = CustomValue[x][y];
10150
10151       CustomValue[x][y] = action_arg_number_new;
10152
10153       if (CustomValue[x][y] != last_ce_value)
10154       {
10155         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10156         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10157
10158         if (CustomValue[x][y] == 0)
10159         {
10160           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10161           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10162         }
10163       }
10164
10165       break;
10166     }
10167
10168     case CA_SET_CE_SCORE:
10169     {
10170       int last_ce_score = ei->collect_score;
10171
10172       ei->collect_score = action_arg_number_new;
10173
10174       if (ei->collect_score != last_ce_score)
10175       {
10176         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10177         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10178
10179         if (ei->collect_score == 0)
10180         {
10181           int xx, yy;
10182
10183           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10184           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10185
10186           /*
10187             This is a very special case that seems to be a mixture between
10188             CheckElementChange() and CheckTriggeredElementChange(): while
10189             the first one only affects single elements that are triggered
10190             directly, the second one affects multiple elements in the playfield
10191             that are triggered indirectly by another element. This is a third
10192             case: Changing the CE score always affects multiple identical CEs,
10193             so every affected CE must be checked, not only the single CE for
10194             which the CE score was changed in the first place (as every instance
10195             of that CE shares the same CE score, and therefore also can change)!
10196           */
10197           SCAN_PLAYFIELD(xx, yy)
10198           {
10199             if (Feld[xx][yy] == element)
10200               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10201                                  CE_SCORE_GETS_ZERO);
10202           }
10203         }
10204       }
10205
10206       break;
10207     }
10208
10209     case CA_SET_CE_ARTWORK:
10210     {
10211       int artwork_element = action_arg_element;
10212       boolean reset_frame = FALSE;
10213       int xx, yy;
10214
10215       if (action_arg == CA_ARG_ELEMENT_RESET)
10216         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10217                            element);
10218
10219       if (ei->gfx_element != artwork_element)
10220         reset_frame = TRUE;
10221
10222       ei->gfx_element = artwork_element;
10223
10224       SCAN_PLAYFIELD(xx, yy)
10225       {
10226         if (Feld[xx][yy] == element)
10227         {
10228           if (reset_frame)
10229           {
10230             ResetGfxAnimation(xx, yy);
10231             ResetRandomAnimationValue(xx, yy);
10232           }
10233
10234           TEST_DrawLevelField(xx, yy);
10235         }
10236       }
10237
10238       break;
10239     }
10240
10241     // ---------- engine actions  ---------------------------------------------
10242
10243     case CA_SET_ENGINE_SCAN_MODE:
10244     {
10245       InitPlayfieldScanMode(action_arg);
10246
10247       break;
10248     }
10249
10250     default:
10251       break;
10252   }
10253 }
10254
10255 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10256 {
10257   int old_element = Feld[x][y];
10258   int new_element = GetElementFromGroupElement(element);
10259   int previous_move_direction = MovDir[x][y];
10260   int last_ce_value = CustomValue[x][y];
10261   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10262   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10263   boolean add_player_onto_element = (new_element_is_player &&
10264                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10265                                      IS_WALKABLE(old_element));
10266
10267   if (!add_player_onto_element)
10268   {
10269     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10270       RemoveMovingField(x, y);
10271     else
10272       RemoveField(x, y);
10273
10274     Feld[x][y] = new_element;
10275
10276     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10277       MovDir[x][y] = previous_move_direction;
10278
10279     if (element_info[new_element].use_last_ce_value)
10280       CustomValue[x][y] = last_ce_value;
10281
10282     InitField_WithBug1(x, y, FALSE);
10283
10284     new_element = Feld[x][y];   // element may have changed
10285
10286     ResetGfxAnimation(x, y);
10287     ResetRandomAnimationValue(x, y);
10288
10289     TEST_DrawLevelField(x, y);
10290
10291     if (GFX_CRUMBLED(new_element))
10292       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10293   }
10294
10295   // check if element under the player changes from accessible to unaccessible
10296   // (needed for special case of dropping element which then changes)
10297   // (must be checked after creating new element for walkable group elements)
10298   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10299       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10300   {
10301     Bang(x, y);
10302
10303     return;
10304   }
10305
10306   // "ChangeCount" not set yet to allow "entered by player" change one time
10307   if (new_element_is_player)
10308     RelocatePlayer(x, y, new_element);
10309
10310   if (is_change)
10311     ChangeCount[x][y]++;        // count number of changes in the same frame
10312
10313   TestIfBadThingTouchesPlayer(x, y);
10314   TestIfPlayerTouchesCustomElement(x, y);
10315   TestIfElementTouchesCustomElement(x, y);
10316 }
10317
10318 static void CreateField(int x, int y, int element)
10319 {
10320   CreateFieldExt(x, y, element, FALSE);
10321 }
10322
10323 static void CreateElementFromChange(int x, int y, int element)
10324 {
10325   element = GET_VALID_RUNTIME_ELEMENT(element);
10326
10327   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10328   {
10329     int old_element = Feld[x][y];
10330
10331     // prevent changed element from moving in same engine frame
10332     // unless both old and new element can either fall or move
10333     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10334         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10335       Stop[x][y] = TRUE;
10336   }
10337
10338   CreateFieldExt(x, y, element, TRUE);
10339 }
10340
10341 static boolean ChangeElement(int x, int y, int element, int page)
10342 {
10343   struct ElementInfo *ei = &element_info[element];
10344   struct ElementChangeInfo *change = &ei->change_page[page];
10345   int ce_value = CustomValue[x][y];
10346   int ce_score = ei->collect_score;
10347   int target_element;
10348   int old_element = Feld[x][y];
10349
10350   // always use default change event to prevent running into a loop
10351   if (ChangeEvent[x][y] == -1)
10352     ChangeEvent[x][y] = CE_DELAY;
10353
10354   if (ChangeEvent[x][y] == CE_DELAY)
10355   {
10356     // reset actual trigger element, trigger player and action element
10357     change->actual_trigger_element = EL_EMPTY;
10358     change->actual_trigger_player = EL_EMPTY;
10359     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10360     change->actual_trigger_side = CH_SIDE_NONE;
10361     change->actual_trigger_ce_value = 0;
10362     change->actual_trigger_ce_score = 0;
10363   }
10364
10365   // do not change elements more than a specified maximum number of changes
10366   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10367     return FALSE;
10368
10369   ChangeCount[x][y]++;          // count number of changes in the same frame
10370
10371   if (change->explode)
10372   {
10373     Bang(x, y);
10374
10375     return TRUE;
10376   }
10377
10378   if (change->use_target_content)
10379   {
10380     boolean complete_replace = TRUE;
10381     boolean can_replace[3][3];
10382     int xx, yy;
10383
10384     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10385     {
10386       boolean is_empty;
10387       boolean is_walkable;
10388       boolean is_diggable;
10389       boolean is_collectible;
10390       boolean is_removable;
10391       boolean is_destructible;
10392       int ex = x + xx - 1;
10393       int ey = y + yy - 1;
10394       int content_element = change->target_content.e[xx][yy];
10395       int e;
10396
10397       can_replace[xx][yy] = TRUE;
10398
10399       if (ex == x && ey == y)   // do not check changing element itself
10400         continue;
10401
10402       if (content_element == EL_EMPTY_SPACE)
10403       {
10404         can_replace[xx][yy] = FALSE;    // do not replace border with space
10405
10406         continue;
10407       }
10408
10409       if (!IN_LEV_FIELD(ex, ey))
10410       {
10411         can_replace[xx][yy] = FALSE;
10412         complete_replace = FALSE;
10413
10414         continue;
10415       }
10416
10417       e = Feld[ex][ey];
10418
10419       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10420         e = MovingOrBlocked2Element(ex, ey);
10421
10422       is_empty = (IS_FREE(ex, ey) ||
10423                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10424
10425       is_walkable     = (is_empty || IS_WALKABLE(e));
10426       is_diggable     = (is_empty || IS_DIGGABLE(e));
10427       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10428       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10429       is_removable    = (is_diggable || is_collectible);
10430
10431       can_replace[xx][yy] =
10432         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10433           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10434           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10435           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10436           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10437           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10438          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10439
10440       if (!can_replace[xx][yy])
10441         complete_replace = FALSE;
10442     }
10443
10444     if (!change->only_if_complete || complete_replace)
10445     {
10446       boolean something_has_changed = FALSE;
10447
10448       if (change->only_if_complete && change->use_random_replace &&
10449           RND(100) < change->random_percentage)
10450         return FALSE;
10451
10452       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10453       {
10454         int ex = x + xx - 1;
10455         int ey = y + yy - 1;
10456         int content_element;
10457
10458         if (can_replace[xx][yy] && (!change->use_random_replace ||
10459                                     RND(100) < change->random_percentage))
10460         {
10461           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10462             RemoveMovingField(ex, ey);
10463
10464           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10465
10466           content_element = change->target_content.e[xx][yy];
10467           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10468                                               ce_value, ce_score);
10469
10470           CreateElementFromChange(ex, ey, target_element);
10471
10472           something_has_changed = TRUE;
10473
10474           // for symmetry reasons, freeze newly created border elements
10475           if (ex != x || ey != y)
10476             Stop[ex][ey] = TRUE;        // no more moving in this frame
10477         }
10478       }
10479
10480       if (something_has_changed)
10481       {
10482         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10483         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10484       }
10485     }
10486   }
10487   else
10488   {
10489     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10490                                         ce_value, ce_score);
10491
10492     if (element == EL_DIAGONAL_GROWING ||
10493         element == EL_DIAGONAL_SHRINKING)
10494     {
10495       target_element = Store[x][y];
10496
10497       Store[x][y] = EL_EMPTY;
10498     }
10499
10500     CreateElementFromChange(x, y, target_element);
10501
10502     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10503     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10504   }
10505
10506   // this uses direct change before indirect change
10507   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10508
10509   return TRUE;
10510 }
10511
10512 static void HandleElementChange(int x, int y, int page)
10513 {
10514   int element = MovingOrBlocked2Element(x, y);
10515   struct ElementInfo *ei = &element_info[element];
10516   struct ElementChangeInfo *change = &ei->change_page[page];
10517   boolean handle_action_before_change = FALSE;
10518
10519 #ifdef DEBUG
10520   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10521       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10522   {
10523     printf("\n\n");
10524     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10525            x, y, element, element_info[element].token_name);
10526     printf("HandleElementChange(): This should never happen!\n");
10527     printf("\n\n");
10528   }
10529 #endif
10530
10531   // this can happen with classic bombs on walkable, changing elements
10532   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10533   {
10534     return;
10535   }
10536
10537   if (ChangeDelay[x][y] == 0)           // initialize element change
10538   {
10539     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10540
10541     if (change->can_change)
10542     {
10543       // !!! not clear why graphic animation should be reset at all here !!!
10544       // !!! UPDATE: but is needed for correct Snake Bite tail animation !!!
10545       // !!! SOLUTION: do not reset if graphics engine set to 4 or above !!!
10546
10547       /*
10548         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10549
10550         When using an animation frame delay of 1 (this only happens with
10551         "sp_zonk.moving.left/right" in the classic graphics), the default
10552         (non-moving) animation shows wrong animation frames (while the
10553         moving animation, like "sp_zonk.moving.left/right", is correct,
10554         so this graphical bug never shows up with the classic graphics).
10555         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10556         be drawn instead of the correct frames 0,1,2,3. This is caused by
10557         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10558         an element change: First when the change delay ("ChangeDelay[][]")
10559         counter has reached zero after decrementing, then a second time in
10560         the next frame (after "GfxFrame[][]" was already incremented) when
10561         "ChangeDelay[][]" is reset to the initial delay value again.
10562
10563         This causes frame 0 to be drawn twice, while the last frame won't
10564         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10565
10566         As some animations may already be cleverly designed around this bug
10567         (at least the "Snake Bite" snake tail animation does this), it cannot
10568         simply be fixed here without breaking such existing animations.
10569         Unfortunately, it cannot easily be detected if a graphics set was
10570         designed "before" or "after" the bug was fixed. As a workaround,
10571         a new graphics set option "game.graphics_engine_version" was added
10572         to be able to specify the game's major release version for which the
10573         graphics set was designed, which can then be used to decide if the
10574         bugfix should be used (version 4 and above) or not (version 3 or
10575         below, or if no version was specified at all, as with old sets).
10576
10577         (The wrong/fixed animation frames can be tested with the test level set
10578         "test_gfxframe" and level "000", which contains a specially prepared
10579         custom element at level position (x/y) == (11/9) which uses the zonk
10580         animation mentioned above. Using "game.graphics_engine_version: 4"
10581         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10582         This can also be seen from the debug output for this test element.)
10583       */
10584
10585       // when a custom element is about to change (for example by change delay),
10586       // do not reset graphic animation when the custom element is moving
10587       if (game.graphics_engine_version < 4 &&
10588           !IS_MOVING(x, y))
10589       {
10590         ResetGfxAnimation(x, y);
10591         ResetRandomAnimationValue(x, y);
10592       }
10593
10594       if (change->pre_change_function)
10595         change->pre_change_function(x, y);
10596     }
10597   }
10598
10599   ChangeDelay[x][y]--;
10600
10601   if (ChangeDelay[x][y] != 0)           // continue element change
10602   {
10603     if (change->can_change)
10604     {
10605       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10606
10607       if (IS_ANIMATED(graphic))
10608         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10609
10610       if (change->change_function)
10611         change->change_function(x, y);
10612     }
10613   }
10614   else                                  // finish element change
10615   {
10616     if (ChangePage[x][y] != -1)         // remember page from delayed change
10617     {
10618       page = ChangePage[x][y];
10619       ChangePage[x][y] = -1;
10620
10621       change = &ei->change_page[page];
10622     }
10623
10624     if (IS_MOVING(x, y))                // never change a running system ;-)
10625     {
10626       ChangeDelay[x][y] = 1;            // try change after next move step
10627       ChangePage[x][y] = page;          // remember page to use for change
10628
10629       return;
10630     }
10631
10632     // special case: set new level random seed before changing element
10633     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10634       handle_action_before_change = TRUE;
10635
10636     if (change->has_action && handle_action_before_change)
10637       ExecuteCustomElementAction(x, y, element, page);
10638
10639     if (change->can_change)
10640     {
10641       if (ChangeElement(x, y, element, page))
10642       {
10643         if (change->post_change_function)
10644           change->post_change_function(x, y);
10645       }
10646     }
10647
10648     if (change->has_action && !handle_action_before_change)
10649       ExecuteCustomElementAction(x, y, element, page);
10650   }
10651 }
10652
10653 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10654                                               int trigger_element,
10655                                               int trigger_event,
10656                                               int trigger_player,
10657                                               int trigger_side,
10658                                               int trigger_page)
10659 {
10660   boolean change_done_any = FALSE;
10661   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10662   int i;
10663
10664   if (!(trigger_events[trigger_element][trigger_event]))
10665     return FALSE;
10666
10667   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10668
10669   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10670   {
10671     int element = EL_CUSTOM_START + i;
10672     boolean change_done = FALSE;
10673     int p;
10674
10675     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10676         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10677       continue;
10678
10679     for (p = 0; p < element_info[element].num_change_pages; p++)
10680     {
10681       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10682
10683       if (change->can_change_or_has_action &&
10684           change->has_event[trigger_event] &&
10685           change->trigger_side & trigger_side &&
10686           change->trigger_player & trigger_player &&
10687           change->trigger_page & trigger_page_bits &&
10688           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10689       {
10690         change->actual_trigger_element = trigger_element;
10691         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10692         change->actual_trigger_player_bits = trigger_player;
10693         change->actual_trigger_side = trigger_side;
10694         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10695         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10696
10697         if ((change->can_change && !change_done) || change->has_action)
10698         {
10699           int x, y;
10700
10701           SCAN_PLAYFIELD(x, y)
10702           {
10703             if (Feld[x][y] == element)
10704             {
10705               if (change->can_change && !change_done)
10706               {
10707                 // if element already changed in this frame, not only prevent
10708                 // another element change (checked in ChangeElement()), but
10709                 // also prevent additional element actions for this element
10710
10711                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10712                     !level.use_action_after_change_bug)
10713                   continue;
10714
10715                 ChangeDelay[x][y] = 1;
10716                 ChangeEvent[x][y] = trigger_event;
10717
10718                 HandleElementChange(x, y, p);
10719               }
10720               else if (change->has_action)
10721               {
10722                 // if element already changed in this frame, not only prevent
10723                 // another element change (checked in ChangeElement()), but
10724                 // also prevent additional element actions for this element
10725
10726                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10727                     !level.use_action_after_change_bug)
10728                   continue;
10729
10730                 ExecuteCustomElementAction(x, y, element, p);
10731                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10732               }
10733             }
10734           }
10735
10736           if (change->can_change)
10737           {
10738             change_done = TRUE;
10739             change_done_any = TRUE;
10740           }
10741         }
10742       }
10743     }
10744   }
10745
10746   RECURSION_LOOP_DETECTION_END();
10747
10748   return change_done_any;
10749 }
10750
10751 static boolean CheckElementChangeExt(int x, int y,
10752                                      int element,
10753                                      int trigger_element,
10754                                      int trigger_event,
10755                                      int trigger_player,
10756                                      int trigger_side)
10757 {
10758   boolean change_done = FALSE;
10759   int p;
10760
10761   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10762       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10763     return FALSE;
10764
10765   if (Feld[x][y] == EL_BLOCKED)
10766   {
10767     Blocked2Moving(x, y, &x, &y);
10768     element = Feld[x][y];
10769   }
10770
10771   // check if element has already changed or is about to change after moving
10772   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10773        Feld[x][y] != element) ||
10774
10775       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10776        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10777         ChangePage[x][y] != -1)))
10778     return FALSE;
10779
10780   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10781
10782   for (p = 0; p < element_info[element].num_change_pages; p++)
10783   {
10784     struct ElementChangeInfo *change = &element_info[element].change_page[p];
10785
10786     /* check trigger element for all events where the element that is checked
10787        for changing interacts with a directly adjacent element -- this is
10788        different to element changes that affect other elements to change on the
10789        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10790     boolean check_trigger_element =
10791       (trigger_event == CE_TOUCHING_X ||
10792        trigger_event == CE_HITTING_X ||
10793        trigger_event == CE_HIT_BY_X ||
10794        trigger_event == CE_DIGGING_X); // this one was forgotten until 3.2.3
10795
10796     if (change->can_change_or_has_action &&
10797         change->has_event[trigger_event] &&
10798         change->trigger_side & trigger_side &&
10799         change->trigger_player & trigger_player &&
10800         (!check_trigger_element ||
10801          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10802     {
10803       change->actual_trigger_element = trigger_element;
10804       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10805       change->actual_trigger_player_bits = trigger_player;
10806       change->actual_trigger_side = trigger_side;
10807       change->actual_trigger_ce_value = CustomValue[x][y];
10808       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10809
10810       // special case: trigger element not at (x,y) position for some events
10811       if (check_trigger_element)
10812       {
10813         static struct
10814         {
10815           int dx, dy;
10816         } move_xy[] =
10817           {
10818             {  0,  0 },
10819             { -1,  0 },
10820             { +1,  0 },
10821             {  0,  0 },
10822             {  0, -1 },
10823             {  0,  0 }, { 0, 0 }, { 0, 0 },
10824             {  0, +1 }
10825           };
10826
10827         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10828         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10829
10830         change->actual_trigger_ce_value = CustomValue[xx][yy];
10831         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10832       }
10833
10834       if (change->can_change && !change_done)
10835       {
10836         ChangeDelay[x][y] = 1;
10837         ChangeEvent[x][y] = trigger_event;
10838
10839         HandleElementChange(x, y, p);
10840
10841         change_done = TRUE;
10842       }
10843       else if (change->has_action)
10844       {
10845         ExecuteCustomElementAction(x, y, element, p);
10846         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10847       }
10848     }
10849   }
10850
10851   RECURSION_LOOP_DETECTION_END();
10852
10853   return change_done;
10854 }
10855
10856 static void PlayPlayerSound(struct PlayerInfo *player)
10857 {
10858   int jx = player->jx, jy = player->jy;
10859   int sound_element = player->artwork_element;
10860   int last_action = player->last_action_waiting;
10861   int action = player->action_waiting;
10862
10863   if (player->is_waiting)
10864   {
10865     if (action != last_action)
10866       PlayLevelSoundElementAction(jx, jy, sound_element, action);
10867     else
10868       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10869   }
10870   else
10871   {
10872     if (action != last_action)
10873       StopSound(element_info[sound_element].sound[last_action]);
10874
10875     if (last_action == ACTION_SLEEPING)
10876       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10877   }
10878 }
10879
10880 static void PlayAllPlayersSound(void)
10881 {
10882   int i;
10883
10884   for (i = 0; i < MAX_PLAYERS; i++)
10885     if (stored_player[i].active)
10886       PlayPlayerSound(&stored_player[i]);
10887 }
10888
10889 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10890 {
10891   boolean last_waiting = player->is_waiting;
10892   int move_dir = player->MovDir;
10893
10894   player->dir_waiting = move_dir;
10895   player->last_action_waiting = player->action_waiting;
10896
10897   if (is_waiting)
10898   {
10899     if (!last_waiting)          // not waiting -> waiting
10900     {
10901       player->is_waiting = TRUE;
10902
10903       player->frame_counter_bored =
10904         FrameCounter +
10905         game.player_boring_delay_fixed +
10906         GetSimpleRandom(game.player_boring_delay_random);
10907       player->frame_counter_sleeping =
10908         FrameCounter +
10909         game.player_sleeping_delay_fixed +
10910         GetSimpleRandom(game.player_sleeping_delay_random);
10911
10912       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10913     }
10914
10915     if (game.player_sleeping_delay_fixed +
10916         game.player_sleeping_delay_random > 0 &&
10917         player->anim_delay_counter == 0 &&
10918         player->post_delay_counter == 0 &&
10919         FrameCounter >= player->frame_counter_sleeping)
10920       player->is_sleeping = TRUE;
10921     else if (game.player_boring_delay_fixed +
10922              game.player_boring_delay_random > 0 &&
10923              FrameCounter >= player->frame_counter_bored)
10924       player->is_bored = TRUE;
10925
10926     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10927                               player->is_bored ? ACTION_BORING :
10928                               ACTION_WAITING);
10929
10930     if (player->is_sleeping && player->use_murphy)
10931     {
10932       // special case for sleeping Murphy when leaning against non-free tile
10933
10934       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10935           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10936            !IS_MOVING(player->jx - 1, player->jy)))
10937         move_dir = MV_LEFT;
10938       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10939                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10940                 !IS_MOVING(player->jx + 1, player->jy)))
10941         move_dir = MV_RIGHT;
10942       else
10943         player->is_sleeping = FALSE;
10944
10945       player->dir_waiting = move_dir;
10946     }
10947
10948     if (player->is_sleeping)
10949     {
10950       if (player->num_special_action_sleeping > 0)
10951       {
10952         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10953         {
10954           int last_special_action = player->special_action_sleeping;
10955           int num_special_action = player->num_special_action_sleeping;
10956           int special_action =
10957             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10958              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10959              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10960              last_special_action + 1 : ACTION_SLEEPING);
10961           int special_graphic =
10962             el_act_dir2img(player->artwork_element, special_action, move_dir);
10963
10964           player->anim_delay_counter =
10965             graphic_info[special_graphic].anim_delay_fixed +
10966             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10967           player->post_delay_counter =
10968             graphic_info[special_graphic].post_delay_fixed +
10969             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10970
10971           player->special_action_sleeping = special_action;
10972         }
10973
10974         if (player->anim_delay_counter > 0)
10975         {
10976           player->action_waiting = player->special_action_sleeping;
10977           player->anim_delay_counter--;
10978         }
10979         else if (player->post_delay_counter > 0)
10980         {
10981           player->post_delay_counter--;
10982         }
10983       }
10984     }
10985     else if (player->is_bored)
10986     {
10987       if (player->num_special_action_bored > 0)
10988       {
10989         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10990         {
10991           int special_action =
10992             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10993           int special_graphic =
10994             el_act_dir2img(player->artwork_element, special_action, move_dir);
10995
10996           player->anim_delay_counter =
10997             graphic_info[special_graphic].anim_delay_fixed +
10998             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10999           player->post_delay_counter =
11000             graphic_info[special_graphic].post_delay_fixed +
11001             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11002
11003           player->special_action_bored = special_action;
11004         }
11005
11006         if (player->anim_delay_counter > 0)
11007         {
11008           player->action_waiting = player->special_action_bored;
11009           player->anim_delay_counter--;
11010         }
11011         else if (player->post_delay_counter > 0)
11012         {
11013           player->post_delay_counter--;
11014         }
11015       }
11016     }
11017   }
11018   else if (last_waiting)        // waiting -> not waiting
11019   {
11020     player->is_waiting = FALSE;
11021     player->is_bored = FALSE;
11022     player->is_sleeping = FALSE;
11023
11024     player->frame_counter_bored = -1;
11025     player->frame_counter_sleeping = -1;
11026
11027     player->anim_delay_counter = 0;
11028     player->post_delay_counter = 0;
11029
11030     player->dir_waiting = player->MovDir;
11031     player->action_waiting = ACTION_DEFAULT;
11032
11033     player->special_action_bored = ACTION_DEFAULT;
11034     player->special_action_sleeping = ACTION_DEFAULT;
11035   }
11036 }
11037
11038 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
11039 {
11040   if ((!player->is_moving  && player->was_moving) ||
11041       (player->MovPos == 0 && player->was_moving) ||
11042       (player->is_snapping && !player->was_snapping) ||
11043       (player->is_dropping && !player->was_dropping))
11044   {
11045     if (!CheckSaveEngineSnapshotToList())
11046       return;
11047
11048     player->was_moving = FALSE;
11049     player->was_snapping = TRUE;
11050     player->was_dropping = TRUE;
11051   }
11052   else
11053   {
11054     if (player->is_moving)
11055       player->was_moving = TRUE;
11056
11057     if (!player->is_snapping)
11058       player->was_snapping = FALSE;
11059
11060     if (!player->is_dropping)
11061       player->was_dropping = FALSE;
11062   }
11063 }
11064
11065 static void CheckSingleStepMode(struct PlayerInfo *player)
11066 {
11067   if (tape.single_step && tape.recording && !tape.pausing)
11068   {
11069     /* as it is called "single step mode", just return to pause mode when the
11070        player stopped moving after one tile (or never starts moving at all) */
11071     if (!player->is_moving &&
11072         !player->is_pushing &&
11073         !player->is_dropping_pressed)
11074       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11075   }
11076
11077   CheckSaveEngineSnapshot(player);
11078 }
11079
11080 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11081 {
11082   int left      = player_action & JOY_LEFT;
11083   int right     = player_action & JOY_RIGHT;
11084   int up        = player_action & JOY_UP;
11085   int down      = player_action & JOY_DOWN;
11086   int button1   = player_action & JOY_BUTTON_1;
11087   int button2   = player_action & JOY_BUTTON_2;
11088   int dx        = (left ? -1 : right ? 1 : 0);
11089   int dy        = (up   ? -1 : down  ? 1 : 0);
11090
11091   if (!player->active || tape.pausing)
11092     return 0;
11093
11094   if (player_action)
11095   {
11096     if (button1)
11097       SnapField(player, dx, dy);
11098     else
11099     {
11100       if (button2)
11101         DropElement(player);
11102
11103       MovePlayer(player, dx, dy);
11104     }
11105
11106     CheckSingleStepMode(player);
11107
11108     SetPlayerWaiting(player, FALSE);
11109
11110     return player_action;
11111   }
11112   else
11113   {
11114     // no actions for this player (no input at player's configured device)
11115
11116     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11117     SnapField(player, 0, 0);
11118     CheckGravityMovementWhenNotMoving(player);
11119
11120     if (player->MovPos == 0)
11121       SetPlayerWaiting(player, TRUE);
11122
11123     if (player->MovPos == 0)    // needed for tape.playing
11124       player->is_moving = FALSE;
11125
11126     player->is_dropping = FALSE;
11127     player->is_dropping_pressed = FALSE;
11128     player->drop_pressed_delay = 0;
11129
11130     CheckSingleStepMode(player);
11131
11132     return 0;
11133   }
11134 }
11135
11136 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11137                                          byte *tape_action)
11138 {
11139   if (!tape.use_mouse)
11140     return;
11141
11142   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11143   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11144   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11145 }
11146
11147 static void SetTapeActionFromMouseAction(byte *tape_action,
11148                                          struct MouseActionInfo *mouse_action)
11149 {
11150   if (!tape.use_mouse)
11151     return;
11152
11153   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11154   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11155   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11156 }
11157
11158 static void CheckLevelSolved(void)
11159 {
11160   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11161   {
11162     if (game_em.level_solved &&
11163         !game_em.game_over)                             // game won
11164     {
11165       LevelSolved();
11166
11167       game_em.game_over = TRUE;
11168
11169       game.all_players_gone = TRUE;
11170     }
11171
11172     if (game_em.game_over)                              // game lost
11173       game.all_players_gone = TRUE;
11174   }
11175   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11176   {
11177     if (game_sp.level_solved &&
11178         !game_sp.game_over)                             // game won
11179     {
11180       LevelSolved();
11181
11182       game_sp.game_over = TRUE;
11183
11184       game.all_players_gone = TRUE;
11185     }
11186
11187     if (game_sp.game_over)                              // game lost
11188       game.all_players_gone = TRUE;
11189   }
11190   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11191   {
11192     if (game_mm.level_solved &&
11193         !game_mm.game_over)                             // game won
11194     {
11195       LevelSolved();
11196
11197       game_mm.game_over = TRUE;
11198
11199       game.all_players_gone = TRUE;
11200     }
11201
11202     if (game_mm.game_over)                              // game lost
11203       game.all_players_gone = TRUE;
11204   }
11205 }
11206
11207 static void CheckLevelTime(void)
11208 {
11209   int i;
11210
11211   if (TimeFrames >= FRAMES_PER_SECOND)
11212   {
11213     TimeFrames = 0;
11214     TapeTime++;
11215
11216     for (i = 0; i < MAX_PLAYERS; i++)
11217     {
11218       struct PlayerInfo *player = &stored_player[i];
11219
11220       if (SHIELD_ON(player))
11221       {
11222         player->shield_normal_time_left--;
11223
11224         if (player->shield_deadly_time_left > 0)
11225           player->shield_deadly_time_left--;
11226       }
11227     }
11228
11229     if (!game.LevelSolved && !level.use_step_counter)
11230     {
11231       TimePlayed++;
11232
11233       if (TimeLeft > 0)
11234       {
11235         TimeLeft--;
11236
11237         if (TimeLeft <= 10 && setup.time_limit)
11238           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11239
11240         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11241            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11242
11243         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11244
11245         if (!TimeLeft && setup.time_limit)
11246         {
11247           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11248             game_em.lev->killed_out_of_time = TRUE;
11249           else
11250             for (i = 0; i < MAX_PLAYERS; i++)
11251               KillPlayer(&stored_player[i]);
11252         }
11253       }
11254       else if (game.no_time_limit && !game.all_players_gone)
11255       {
11256         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11257       }
11258
11259       game_em.lev->time = (game.no_time_limit ? TimePlayed : TimeLeft);
11260     }
11261
11262     if (tape.recording || tape.playing)
11263       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11264   }
11265
11266   if (tape.recording || tape.playing)
11267     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11268
11269   UpdateAndDisplayGameControlValues();
11270 }
11271
11272 void AdvanceFrameAndPlayerCounters(int player_nr)
11273 {
11274   int i;
11275
11276   // advance frame counters (global frame counter and time frame counter)
11277   FrameCounter++;
11278   TimeFrames++;
11279
11280   // advance player counters (counters for move delay, move animation etc.)
11281   for (i = 0; i < MAX_PLAYERS; i++)
11282   {
11283     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11284     int move_delay_value = stored_player[i].move_delay_value;
11285     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11286
11287     if (!advance_player_counters)       // not all players may be affected
11288       continue;
11289
11290     if (move_frames == 0)       // less than one move per game frame
11291     {
11292       int stepsize = TILEX / move_delay_value;
11293       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11294       int count = (stored_player[i].is_moving ?
11295                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11296
11297       if (count % delay == 0)
11298         move_frames = 1;
11299     }
11300
11301     stored_player[i].Frame += move_frames;
11302
11303     if (stored_player[i].MovPos != 0)
11304       stored_player[i].StepFrame += move_frames;
11305
11306     if (stored_player[i].move_delay > 0)
11307       stored_player[i].move_delay--;
11308
11309     // due to bugs in previous versions, counter must count up, not down
11310     if (stored_player[i].push_delay != -1)
11311       stored_player[i].push_delay++;
11312
11313     if (stored_player[i].drop_delay > 0)
11314       stored_player[i].drop_delay--;
11315
11316     if (stored_player[i].is_dropping_pressed)
11317       stored_player[i].drop_pressed_delay++;
11318   }
11319 }
11320
11321 void StartGameActions(boolean init_network_game, boolean record_tape,
11322                       int random_seed)
11323 {
11324   unsigned int new_random_seed = InitRND(random_seed);
11325
11326   if (record_tape)
11327     TapeStartRecording(new_random_seed);
11328
11329   if (init_network_game)
11330   {
11331     SendToServer_LevelFile();
11332     SendToServer_StartPlaying();
11333
11334     return;
11335   }
11336
11337   InitGame();
11338 }
11339
11340 static void GameActionsExt(void)
11341 {
11342 #if 0
11343   static unsigned int game_frame_delay = 0;
11344 #endif
11345   unsigned int game_frame_delay_value;
11346   byte *recorded_player_action;
11347   byte summarized_player_action = 0;
11348   byte tape_action[MAX_PLAYERS];
11349   int i;
11350
11351   // detect endless loops, caused by custom element programming
11352   if (recursion_loop_detected && recursion_loop_depth == 0)
11353   {
11354     char *message = getStringCat3("Internal Error! Element ",
11355                                   EL_NAME(recursion_loop_element),
11356                                   " caused endless loop! Quit the game?");
11357
11358     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11359           EL_NAME(recursion_loop_element));
11360
11361     RequestQuitGameExt(FALSE, level_editor_test_game, message);
11362
11363     recursion_loop_detected = FALSE;    // if game should be continued
11364
11365     free(message);
11366
11367     return;
11368   }
11369
11370   if (game.restart_level)
11371     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11372
11373   CheckLevelSolved();
11374
11375   if (game.LevelSolved && !game.LevelSolved_GameEnd)
11376     GameWon();
11377
11378   if (game.all_players_gone && !TAPE_IS_STOPPED(tape))
11379     TapeStop();
11380
11381   if (game_status != GAME_MODE_PLAYING)         // status might have changed
11382     return;
11383
11384   game_frame_delay_value =
11385     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11386
11387   if (tape.playing && tape.warp_forward && !tape.pausing)
11388     game_frame_delay_value = 0;
11389
11390   SetVideoFrameDelay(game_frame_delay_value);
11391
11392   // (de)activate virtual buttons depending on current game status
11393   if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
11394   {
11395     if (game.all_players_gone)  // if no players there to be controlled anymore
11396       SetOverlayActive(FALSE);
11397     else if (!tape.playing)     // if game continues after tape stopped playing
11398       SetOverlayActive(TRUE);
11399   }
11400
11401 #if 0
11402 #if 0
11403   // ---------- main game synchronization point ----------
11404
11405   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11406
11407   printf("::: skip == %d\n", skip);
11408
11409 #else
11410   // ---------- main game synchronization point ----------
11411
11412   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11413 #endif
11414 #endif
11415
11416   if (network_playing && !network_player_action_received)
11417   {
11418     // try to get network player actions in time
11419
11420     // last chance to get network player actions without main loop delay
11421     HandleNetworking();
11422
11423     // game was quit by network peer
11424     if (game_status != GAME_MODE_PLAYING)
11425       return;
11426
11427     // check if network player actions still missing and game still running
11428     if (!network_player_action_received && !checkGameEnded())
11429       return;           // failed to get network player actions in time
11430
11431     // do not yet reset "network_player_action_received" (for tape.pausing)
11432   }
11433
11434   if (tape.pausing)
11435     return;
11436
11437   // at this point we know that we really continue executing the game
11438
11439   network_player_action_received = FALSE;
11440
11441   // when playing tape, read previously recorded player input from tape data
11442   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11443
11444   local_player->effective_mouse_action = local_player->mouse_action;
11445
11446   if (recorded_player_action != NULL)
11447     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11448                                  recorded_player_action);
11449
11450   // TapePlayAction() may return NULL when toggling to "pause before death"
11451   if (tape.pausing)
11452     return;
11453
11454   if (tape.set_centered_player)
11455   {
11456     game.centered_player_nr_next = tape.centered_player_nr_next;
11457     game.set_centered_player = TRUE;
11458   }
11459
11460   for (i = 0; i < MAX_PLAYERS; i++)
11461   {
11462     summarized_player_action |= stored_player[i].action;
11463
11464     if (!network_playing && (game.team_mode || tape.playing))
11465       stored_player[i].effective_action = stored_player[i].action;
11466   }
11467
11468   if (network_playing && !checkGameEnded())
11469     SendToServer_MovePlayer(summarized_player_action);
11470
11471   // summarize all actions at local players mapped input device position
11472   // (this allows using different input devices in single player mode)
11473   if (!network.enabled && !game.team_mode)
11474     stored_player[map_player_action[local_player->index_nr]].effective_action =
11475       summarized_player_action;
11476
11477   // summarize all actions at centered player in local team mode
11478   if (tape.recording &&
11479       setup.team_mode && !network.enabled &&
11480       setup.input_on_focus &&
11481       game.centered_player_nr != -1)
11482   {
11483     for (i = 0; i < MAX_PLAYERS; i++)
11484       stored_player[map_player_action[i]].effective_action =
11485         (i == game.centered_player_nr ? summarized_player_action : 0);
11486   }
11487
11488   if (recorded_player_action != NULL)
11489     for (i = 0; i < MAX_PLAYERS; i++)
11490       stored_player[i].effective_action = recorded_player_action[i];
11491
11492   for (i = 0; i < MAX_PLAYERS; i++)
11493   {
11494     tape_action[i] = stored_player[i].effective_action;
11495
11496     /* (this may happen in the RND game engine if a player was not present on
11497        the playfield on level start, but appeared later from a custom element */
11498     if (setup.team_mode &&
11499         tape.recording &&
11500         tape_action[i] &&
11501         !tape.player_participates[i])
11502       tape.player_participates[i] = TRUE;
11503   }
11504
11505   SetTapeActionFromMouseAction(tape_action,
11506                                &local_player->effective_mouse_action);
11507
11508   // only record actions from input devices, but not programmed actions
11509   if (tape.recording)
11510     TapeRecordAction(tape_action);
11511
11512   // remember if game was played (especially after tape stopped playing)
11513   if (!tape.playing && summarized_player_action)
11514     game.GamePlayed = TRUE;
11515
11516 #if USE_NEW_PLAYER_ASSIGNMENTS
11517   // !!! also map player actions in single player mode !!!
11518   // if (game.team_mode)
11519   if (1)
11520   {
11521     byte mapped_action[MAX_PLAYERS];
11522
11523 #if DEBUG_PLAYER_ACTIONS
11524     printf(":::");
11525     for (i = 0; i < MAX_PLAYERS; i++)
11526       printf(" %d, ", stored_player[i].effective_action);
11527 #endif
11528
11529     for (i = 0; i < MAX_PLAYERS; i++)
11530       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11531
11532     for (i = 0; i < MAX_PLAYERS; i++)
11533       stored_player[i].effective_action = mapped_action[i];
11534
11535 #if DEBUG_PLAYER_ACTIONS
11536     printf(" =>");
11537     for (i = 0; i < MAX_PLAYERS; i++)
11538       printf(" %d, ", stored_player[i].effective_action);
11539     printf("\n");
11540 #endif
11541   }
11542 #if DEBUG_PLAYER_ACTIONS
11543   else
11544   {
11545     printf(":::");
11546     for (i = 0; i < MAX_PLAYERS; i++)
11547       printf(" %d, ", stored_player[i].effective_action);
11548     printf("\n");
11549   }
11550 #endif
11551 #endif
11552
11553   for (i = 0; i < MAX_PLAYERS; i++)
11554   {
11555     // allow engine snapshot in case of changed movement attempt
11556     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11557         (stored_player[i].effective_action & KEY_MOTION))
11558       game.snapshot.changed_action = TRUE;
11559
11560     // allow engine snapshot in case of snapping/dropping attempt
11561     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11562         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11563       game.snapshot.changed_action = TRUE;
11564
11565     game.snapshot.last_action[i] = stored_player[i].effective_action;
11566   }
11567
11568   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11569   {
11570     GameActions_EM_Main();
11571   }
11572   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11573   {
11574     GameActions_SP_Main();
11575   }
11576   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11577   {
11578     GameActions_MM_Main();
11579   }
11580   else
11581   {
11582     GameActions_RND_Main();
11583   }
11584
11585   BlitScreenToBitmap(backbuffer);
11586
11587   CheckLevelSolved();
11588   CheckLevelTime();
11589
11590   AdvanceFrameAndPlayerCounters(-1);    // advance counters for all players
11591
11592   if (global.show_frames_per_second)
11593   {
11594     static unsigned int fps_counter = 0;
11595     static int fps_frames = 0;
11596     unsigned int fps_delay_ms = Counter() - fps_counter;
11597
11598     fps_frames++;
11599
11600     if (fps_delay_ms >= 500)    // calculate FPS every 0.5 seconds
11601     {
11602       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11603
11604       fps_frames = 0;
11605       fps_counter = Counter();
11606
11607       // always draw FPS to screen after FPS value was updated
11608       redraw_mask |= REDRAW_FPS;
11609     }
11610
11611     // only draw FPS if no screen areas are deactivated (invisible warp mode)
11612     if (GetDrawDeactivationMask() == REDRAW_NONE)
11613       redraw_mask |= REDRAW_FPS;
11614   }
11615 }
11616
11617 static void GameActions_CheckSaveEngineSnapshot(void)
11618 {
11619   if (!game.snapshot.save_snapshot)
11620     return;
11621
11622   // clear flag for saving snapshot _before_ saving snapshot
11623   game.snapshot.save_snapshot = FALSE;
11624
11625   SaveEngineSnapshotToList();
11626 }
11627
11628 void GameActions(void)
11629 {
11630   GameActionsExt();
11631
11632   GameActions_CheckSaveEngineSnapshot();
11633 }
11634
11635 void GameActions_EM_Main(void)
11636 {
11637   byte effective_action[MAX_PLAYERS];
11638   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11639   int i;
11640
11641   for (i = 0; i < MAX_PLAYERS; i++)
11642     effective_action[i] = stored_player[i].effective_action;
11643
11644   GameActions_EM(effective_action, warp_mode);
11645 }
11646
11647 void GameActions_SP_Main(void)
11648 {
11649   byte effective_action[MAX_PLAYERS];
11650   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11651   int i;
11652
11653   for (i = 0; i < MAX_PLAYERS; i++)
11654     effective_action[i] = stored_player[i].effective_action;
11655
11656   GameActions_SP(effective_action, warp_mode);
11657
11658   for (i = 0; i < MAX_PLAYERS; i++)
11659   {
11660     if (stored_player[i].force_dropping)
11661       stored_player[i].action |= KEY_BUTTON_DROP;
11662
11663     stored_player[i].force_dropping = FALSE;
11664   }
11665 }
11666
11667 void GameActions_MM_Main(void)
11668 {
11669   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11670
11671   GameActions_MM(local_player->effective_mouse_action, warp_mode);
11672 }
11673
11674 void GameActions_RND_Main(void)
11675 {
11676   GameActions_RND();
11677 }
11678
11679 void GameActions_RND(void)
11680 {
11681   int magic_wall_x = 0, magic_wall_y = 0;
11682   int i, x, y, element, graphic, last_gfx_frame;
11683
11684   InitPlayfieldScanModeVars();
11685
11686   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11687   {
11688     SCAN_PLAYFIELD(x, y)
11689     {
11690       ChangeCount[x][y] = 0;
11691       ChangeEvent[x][y] = -1;
11692     }
11693   }
11694
11695   if (game.set_centered_player)
11696   {
11697     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11698
11699     // switching to "all players" only possible if all players fit to screen
11700     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11701     {
11702       game.centered_player_nr_next = game.centered_player_nr;
11703       game.set_centered_player = FALSE;
11704     }
11705
11706     // do not switch focus to non-existing (or non-active) player
11707     if (game.centered_player_nr_next >= 0 &&
11708         !stored_player[game.centered_player_nr_next].active)
11709     {
11710       game.centered_player_nr_next = game.centered_player_nr;
11711       game.set_centered_player = FALSE;
11712     }
11713   }
11714
11715   if (game.set_centered_player &&
11716       ScreenMovPos == 0)        // screen currently aligned at tile position
11717   {
11718     int sx, sy;
11719
11720     if (game.centered_player_nr_next == -1)
11721     {
11722       setScreenCenteredToAllPlayers(&sx, &sy);
11723     }
11724     else
11725     {
11726       sx = stored_player[game.centered_player_nr_next].jx;
11727       sy = stored_player[game.centered_player_nr_next].jy;
11728     }
11729
11730     game.centered_player_nr = game.centered_player_nr_next;
11731     game.set_centered_player = FALSE;
11732
11733     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11734     DrawGameDoorValues();
11735   }
11736
11737   for (i = 0; i < MAX_PLAYERS; i++)
11738   {
11739     int actual_player_action = stored_player[i].effective_action;
11740
11741 #if 1
11742     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11743        - rnd_equinox_tetrachloride 048
11744        - rnd_equinox_tetrachloride_ii 096
11745        - rnd_emanuel_schmieg 002
11746        - doctor_sloan_ww 001, 020
11747     */
11748     if (stored_player[i].MovPos == 0)
11749       CheckGravityMovement(&stored_player[i]);
11750 #endif
11751
11752     // overwrite programmed action with tape action
11753     if (stored_player[i].programmed_action)
11754       actual_player_action = stored_player[i].programmed_action;
11755
11756     PlayerActions(&stored_player[i], actual_player_action);
11757
11758     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11759   }
11760
11761   ScrollScreen(NULL, SCROLL_GO_ON);
11762
11763   /* for backwards compatibility, the following code emulates a fixed bug that
11764      occured when pushing elements (causing elements that just made their last
11765      pushing step to already (if possible) make their first falling step in the
11766      same game frame, which is bad); this code is also needed to use the famous
11767      "spring push bug" which is used in older levels and might be wanted to be
11768      used also in newer levels, but in this case the buggy pushing code is only
11769      affecting the "spring" element and no other elements */
11770
11771   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11772   {
11773     for (i = 0; i < MAX_PLAYERS; i++)
11774     {
11775       struct PlayerInfo *player = &stored_player[i];
11776       int x = player->jx;
11777       int y = player->jy;
11778
11779       if (player->active && player->is_pushing && player->is_moving &&
11780           IS_MOVING(x, y) &&
11781           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11782            Feld[x][y] == EL_SPRING))
11783       {
11784         ContinueMoving(x, y);
11785
11786         // continue moving after pushing (this is actually a bug)
11787         if (!IS_MOVING(x, y))
11788           Stop[x][y] = FALSE;
11789       }
11790     }
11791   }
11792
11793   SCAN_PLAYFIELD(x, y)
11794   {
11795     Last[x][y] = Feld[x][y];
11796
11797     ChangeCount[x][y] = 0;
11798     ChangeEvent[x][y] = -1;
11799
11800     // this must be handled before main playfield loop
11801     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11802     {
11803       MovDelay[x][y]--;
11804       if (MovDelay[x][y] <= 0)
11805         RemoveField(x, y);
11806     }
11807
11808     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11809     {
11810       MovDelay[x][y]--;
11811       if (MovDelay[x][y] <= 0)
11812       {
11813         RemoveField(x, y);
11814         TEST_DrawLevelField(x, y);
11815
11816         TestIfElementTouchesCustomElement(x, y);        // for empty space
11817       }
11818     }
11819
11820 #if DEBUG
11821     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11822     {
11823       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11824       printf("GameActions(): This should never happen!\n");
11825
11826       ChangePage[x][y] = -1;
11827     }
11828 #endif
11829
11830     Stop[x][y] = FALSE;
11831     if (WasJustMoving[x][y] > 0)
11832       WasJustMoving[x][y]--;
11833     if (WasJustFalling[x][y] > 0)
11834       WasJustFalling[x][y]--;
11835     if (CheckCollision[x][y] > 0)
11836       CheckCollision[x][y]--;
11837     if (CheckImpact[x][y] > 0)
11838       CheckImpact[x][y]--;
11839
11840     GfxFrame[x][y]++;
11841
11842     /* reset finished pushing action (not done in ContinueMoving() to allow
11843        continuous pushing animation for elements with zero push delay) */
11844     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11845     {
11846       ResetGfxAnimation(x, y);
11847       TEST_DrawLevelField(x, y);
11848     }
11849
11850 #if DEBUG
11851     if (IS_BLOCKED(x, y))
11852     {
11853       int oldx, oldy;
11854
11855       Blocked2Moving(x, y, &oldx, &oldy);
11856       if (!IS_MOVING(oldx, oldy))
11857       {
11858         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11859         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11860         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11861         printf("GameActions(): This should never happen!\n");
11862       }
11863     }
11864 #endif
11865   }
11866
11867   SCAN_PLAYFIELD(x, y)
11868   {
11869     element = Feld[x][y];
11870     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11871     last_gfx_frame = GfxFrame[x][y];
11872
11873     ResetGfxFrame(x, y);
11874
11875     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
11876       DrawLevelGraphicAnimation(x, y, graphic);
11877
11878     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11879         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11880       ResetRandomAnimationValue(x, y);
11881
11882     SetRandomAnimationValue(x, y);
11883
11884     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11885
11886     if (IS_INACTIVE(element))
11887     {
11888       if (IS_ANIMATED(graphic))
11889         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11890
11891       continue;
11892     }
11893
11894     // this may take place after moving, so 'element' may have changed
11895     if (IS_CHANGING(x, y) &&
11896         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11897     {
11898       int page = element_info[element].event_page_nr[CE_DELAY];
11899
11900       HandleElementChange(x, y, page);
11901
11902       element = Feld[x][y];
11903       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11904     }
11905
11906     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11907     {
11908       StartMoving(x, y);
11909
11910       element = Feld[x][y];
11911       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11912
11913       if (IS_ANIMATED(graphic) &&
11914           !IS_MOVING(x, y) &&
11915           !Stop[x][y])
11916         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11917
11918       if (IS_GEM(element) || element == EL_SP_INFOTRON)
11919         TEST_DrawTwinkleOnField(x, y);
11920     }
11921     else if (element == EL_ACID)
11922     {
11923       if (!Stop[x][y])
11924         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11925     }
11926     else if ((element == EL_EXIT_OPEN ||
11927               element == EL_EM_EXIT_OPEN ||
11928               element == EL_SP_EXIT_OPEN ||
11929               element == EL_STEEL_EXIT_OPEN ||
11930               element == EL_EM_STEEL_EXIT_OPEN ||
11931               element == EL_SP_TERMINAL ||
11932               element == EL_SP_TERMINAL_ACTIVE ||
11933               element == EL_EXTRA_TIME ||
11934               element == EL_SHIELD_NORMAL ||
11935               element == EL_SHIELD_DEADLY) &&
11936              IS_ANIMATED(graphic))
11937       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11938     else if (IS_MOVING(x, y))
11939       ContinueMoving(x, y);
11940     else if (IS_ACTIVE_BOMB(element))
11941       CheckDynamite(x, y);
11942     else if (element == EL_AMOEBA_GROWING)
11943       AmoebeWaechst(x, y);
11944     else if (element == EL_AMOEBA_SHRINKING)
11945       AmoebaDisappearing(x, y);
11946
11947 #if !USE_NEW_AMOEBA_CODE
11948     else if (IS_AMOEBALIVE(element))
11949       AmoebeAbleger(x, y);
11950 #endif
11951
11952     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11953       Life(x, y);
11954     else if (element == EL_EXIT_CLOSED)
11955       CheckExit(x, y);
11956     else if (element == EL_EM_EXIT_CLOSED)
11957       CheckExitEM(x, y);
11958     else if (element == EL_STEEL_EXIT_CLOSED)
11959       CheckExitSteel(x, y);
11960     else if (element == EL_EM_STEEL_EXIT_CLOSED)
11961       CheckExitSteelEM(x, y);
11962     else if (element == EL_SP_EXIT_CLOSED)
11963       CheckExitSP(x, y);
11964     else if (element == EL_EXPANDABLE_WALL_GROWING ||
11965              element == EL_EXPANDABLE_STEELWALL_GROWING)
11966       MauerWaechst(x, y);
11967     else if (element == EL_EXPANDABLE_WALL ||
11968              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11969              element == EL_EXPANDABLE_WALL_VERTICAL ||
11970              element == EL_EXPANDABLE_WALL_ANY ||
11971              element == EL_BD_EXPANDABLE_WALL)
11972       MauerAbleger(x, y);
11973     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11974              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11975              element == EL_EXPANDABLE_STEELWALL_ANY)
11976       MauerAblegerStahl(x, y);
11977     else if (element == EL_FLAMES)
11978       CheckForDragon(x, y);
11979     else if (element == EL_EXPLOSION)
11980       ; // drawing of correct explosion animation is handled separately
11981     else if (element == EL_ELEMENT_SNAPPING ||
11982              element == EL_DIAGONAL_SHRINKING ||
11983              element == EL_DIAGONAL_GROWING)
11984     {
11985       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11986
11987       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11988     }
11989     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11990       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11991
11992     if (IS_BELT_ACTIVE(element))
11993       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11994
11995     if (game.magic_wall_active)
11996     {
11997       int jx = local_player->jx, jy = local_player->jy;
11998
11999       // play the element sound at the position nearest to the player
12000       if ((element == EL_MAGIC_WALL_FULL ||
12001            element == EL_MAGIC_WALL_ACTIVE ||
12002            element == EL_MAGIC_WALL_EMPTYING ||
12003            element == EL_BD_MAGIC_WALL_FULL ||
12004            element == EL_BD_MAGIC_WALL_ACTIVE ||
12005            element == EL_BD_MAGIC_WALL_EMPTYING ||
12006            element == EL_DC_MAGIC_WALL_FULL ||
12007            element == EL_DC_MAGIC_WALL_ACTIVE ||
12008            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12009           ABS(x - jx) + ABS(y - jy) <
12010           ABS(magic_wall_x - jx) + ABS(magic_wall_y - jy))
12011       {
12012         magic_wall_x = x;
12013         magic_wall_y = y;
12014       }
12015     }
12016   }
12017
12018 #if USE_NEW_AMOEBA_CODE
12019   // new experimental amoeba growth stuff
12020   if (!(FrameCounter % 8))
12021   {
12022     static unsigned int random = 1684108901;
12023
12024     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12025     {
12026       x = RND(lev_fieldx);
12027       y = RND(lev_fieldy);
12028       element = Feld[x][y];
12029
12030       if (!IS_PLAYER(x,y) &&
12031           (element == EL_EMPTY ||
12032            CAN_GROW_INTO(element) ||
12033            element == EL_QUICKSAND_EMPTY ||
12034            element == EL_QUICKSAND_FAST_EMPTY ||
12035            element == EL_ACID_SPLASH_LEFT ||
12036            element == EL_ACID_SPLASH_RIGHT))
12037       {
12038         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
12039             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
12040             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
12041             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
12042           Feld[x][y] = EL_AMOEBA_DROP;
12043       }
12044
12045       random = random * 129 + 1;
12046     }
12047   }
12048 #endif
12049
12050   game.explosions_delayed = FALSE;
12051
12052   SCAN_PLAYFIELD(x, y)
12053   {
12054     element = Feld[x][y];
12055
12056     if (ExplodeField[x][y])
12057       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12058     else if (element == EL_EXPLOSION)
12059       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12060
12061     ExplodeField[x][y] = EX_TYPE_NONE;
12062   }
12063
12064   game.explosions_delayed = TRUE;
12065
12066   if (game.magic_wall_active)
12067   {
12068     if (!(game.magic_wall_time_left % 4))
12069     {
12070       int element = Feld[magic_wall_x][magic_wall_y];
12071
12072       if (element == EL_BD_MAGIC_WALL_FULL ||
12073           element == EL_BD_MAGIC_WALL_ACTIVE ||
12074           element == EL_BD_MAGIC_WALL_EMPTYING)
12075         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12076       else if (element == EL_DC_MAGIC_WALL_FULL ||
12077                element == EL_DC_MAGIC_WALL_ACTIVE ||
12078                element == EL_DC_MAGIC_WALL_EMPTYING)
12079         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12080       else
12081         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12082     }
12083
12084     if (game.magic_wall_time_left > 0)
12085     {
12086       game.magic_wall_time_left--;
12087
12088       if (!game.magic_wall_time_left)
12089       {
12090         SCAN_PLAYFIELD(x, y)
12091         {
12092           element = Feld[x][y];
12093
12094           if (element == EL_MAGIC_WALL_ACTIVE ||
12095               element == EL_MAGIC_WALL_FULL)
12096           {
12097             Feld[x][y] = EL_MAGIC_WALL_DEAD;
12098             TEST_DrawLevelField(x, y);
12099           }
12100           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12101                    element == EL_BD_MAGIC_WALL_FULL)
12102           {
12103             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
12104             TEST_DrawLevelField(x, y);
12105           }
12106           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12107                    element == EL_DC_MAGIC_WALL_FULL)
12108           {
12109             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
12110             TEST_DrawLevelField(x, y);
12111           }
12112         }
12113
12114         game.magic_wall_active = FALSE;
12115       }
12116     }
12117   }
12118
12119   if (game.light_time_left > 0)
12120   {
12121     game.light_time_left--;
12122
12123     if (game.light_time_left == 0)
12124       RedrawAllLightSwitchesAndInvisibleElements();
12125   }
12126
12127   if (game.timegate_time_left > 0)
12128   {
12129     game.timegate_time_left--;
12130
12131     if (game.timegate_time_left == 0)
12132       CloseAllOpenTimegates();
12133   }
12134
12135   if (game.lenses_time_left > 0)
12136   {
12137     game.lenses_time_left--;
12138
12139     if (game.lenses_time_left == 0)
12140       RedrawAllInvisibleElementsForLenses();
12141   }
12142
12143   if (game.magnify_time_left > 0)
12144   {
12145     game.magnify_time_left--;
12146
12147     if (game.magnify_time_left == 0)
12148       RedrawAllInvisibleElementsForMagnifier();
12149   }
12150
12151   for (i = 0; i < MAX_PLAYERS; i++)
12152   {
12153     struct PlayerInfo *player = &stored_player[i];
12154
12155     if (SHIELD_ON(player))
12156     {
12157       if (player->shield_deadly_time_left)
12158         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12159       else if (player->shield_normal_time_left)
12160         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12161     }
12162   }
12163
12164 #if USE_DELAYED_GFX_REDRAW
12165   SCAN_PLAYFIELD(x, y)
12166   {
12167     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12168     {
12169       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12170          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12171
12172       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12173         DrawLevelField(x, y);
12174
12175       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12176         DrawLevelFieldCrumbled(x, y);
12177
12178       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12179         DrawLevelFieldCrumbledNeighbours(x, y);
12180
12181       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12182         DrawTwinkleOnField(x, y);
12183     }
12184
12185     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12186   }
12187 #endif
12188
12189   DrawAllPlayers();
12190   PlayAllPlayersSound();
12191
12192   for (i = 0; i < MAX_PLAYERS; i++)
12193   {
12194     struct PlayerInfo *player = &stored_player[i];
12195
12196     if (player->show_envelope != 0 && (!player->active ||
12197                                        player->MovPos == 0))
12198     {
12199       ShowEnvelope(player->show_envelope - EL_ENVELOPE_1);
12200
12201       player->show_envelope = 0;
12202     }
12203   }
12204
12205   // use random number generator in every frame to make it less predictable
12206   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12207     RND(1);
12208 }
12209
12210 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12211 {
12212   int min_x = x, min_y = y, max_x = x, max_y = y;
12213   int i;
12214
12215   for (i = 0; i < MAX_PLAYERS; i++)
12216   {
12217     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12218
12219     if (!stored_player[i].active || &stored_player[i] == player)
12220       continue;
12221
12222     min_x = MIN(min_x, jx);
12223     min_y = MIN(min_y, jy);
12224     max_x = MAX(max_x, jx);
12225     max_y = MAX(max_y, jy);
12226   }
12227
12228   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12229 }
12230
12231 static boolean AllPlayersInVisibleScreen(void)
12232 {
12233   int i;
12234
12235   for (i = 0; i < MAX_PLAYERS; i++)
12236   {
12237     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12238
12239     if (!stored_player[i].active)
12240       continue;
12241
12242     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12243       return FALSE;
12244   }
12245
12246   return TRUE;
12247 }
12248
12249 void ScrollLevel(int dx, int dy)
12250 {
12251   int scroll_offset = 2 * TILEX_VAR;
12252   int x, y;
12253
12254   BlitBitmap(drawto_field, drawto_field,
12255              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12256              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12257              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12258              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12259              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12260              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12261
12262   if (dx != 0)
12263   {
12264     x = (dx == 1 ? BX1 : BX2);
12265     for (y = BY1; y <= BY2; y++)
12266       DrawScreenField(x, y);
12267   }
12268
12269   if (dy != 0)
12270   {
12271     y = (dy == 1 ? BY1 : BY2);
12272     for (x = BX1; x <= BX2; x++)
12273       DrawScreenField(x, y);
12274   }
12275
12276   redraw_mask |= REDRAW_FIELD;
12277 }
12278
12279 static boolean canFallDown(struct PlayerInfo *player)
12280 {
12281   int jx = player->jx, jy = player->jy;
12282
12283   return (IN_LEV_FIELD(jx, jy + 1) &&
12284           (IS_FREE(jx, jy + 1) ||
12285            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12286           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
12287           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
12288 }
12289
12290 static boolean canPassField(int x, int y, int move_dir)
12291 {
12292   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12293   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12294   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12295   int nextx = x + dx;
12296   int nexty = y + dy;
12297   int element = Feld[x][y];
12298
12299   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12300           !CAN_MOVE(element) &&
12301           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12302           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
12303           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12304 }
12305
12306 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12307 {
12308   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12309   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12310   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12311   int newx = x + dx;
12312   int newy = y + dy;
12313
12314   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12315           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12316           (IS_DIGGABLE(Feld[newx][newy]) ||
12317            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12318            canPassField(newx, newy, move_dir)));
12319 }
12320
12321 static void CheckGravityMovement(struct PlayerInfo *player)
12322 {
12323   if (player->gravity && !player->programmed_action)
12324   {
12325     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12326     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12327     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12328     int jx = player->jx, jy = player->jy;
12329     boolean player_is_moving_to_valid_field =
12330       (!player_is_snapping &&
12331        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12332         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12333     boolean player_can_fall_down = canFallDown(player);
12334
12335     if (player_can_fall_down &&
12336         !player_is_moving_to_valid_field)
12337       player->programmed_action = MV_DOWN;
12338   }
12339 }
12340
12341 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12342 {
12343   return CheckGravityMovement(player);
12344
12345   if (player->gravity && !player->programmed_action)
12346   {
12347     int jx = player->jx, jy = player->jy;
12348     boolean field_under_player_is_free =
12349       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12350     boolean player_is_standing_on_valid_field =
12351       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12352        (IS_WALKABLE(Feld[jx][jy]) &&
12353         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12354
12355     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12356       player->programmed_action = MV_DOWN;
12357   }
12358 }
12359
12360 /*
12361   MovePlayerOneStep()
12362   -----------------------------------------------------------------------------
12363   dx, dy:               direction (non-diagonal) to try to move the player to
12364   real_dx, real_dy:     direction as read from input device (can be diagonal)
12365 */
12366
12367 boolean MovePlayerOneStep(struct PlayerInfo *player,
12368                           int dx, int dy, int real_dx, int real_dy)
12369 {
12370   int jx = player->jx, jy = player->jy;
12371   int new_jx = jx + dx, new_jy = jy + dy;
12372   int can_move;
12373   boolean player_can_move = !player->cannot_move;
12374
12375   if (!player->active || (!dx && !dy))
12376     return MP_NO_ACTION;
12377
12378   player->MovDir = (dx < 0 ? MV_LEFT :
12379                     dx > 0 ? MV_RIGHT :
12380                     dy < 0 ? MV_UP :
12381                     dy > 0 ? MV_DOWN :  MV_NONE);
12382
12383   if (!IN_LEV_FIELD(new_jx, new_jy))
12384     return MP_NO_ACTION;
12385
12386   if (!player_can_move)
12387   {
12388     if (player->MovPos == 0)
12389     {
12390       player->is_moving = FALSE;
12391       player->is_digging = FALSE;
12392       player->is_collecting = FALSE;
12393       player->is_snapping = FALSE;
12394       player->is_pushing = FALSE;
12395     }
12396   }
12397
12398   if (!network.enabled && game.centered_player_nr == -1 &&
12399       !AllPlayersInSight(player, new_jx, new_jy))
12400     return MP_NO_ACTION;
12401
12402   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12403   if (can_move != MP_MOVING)
12404     return can_move;
12405
12406   // check if DigField() has caused relocation of the player
12407   if (player->jx != jx || player->jy != jy)
12408     return MP_NO_ACTION;        // <-- !!! CHECK THIS [-> MP_ACTION ?] !!!
12409
12410   StorePlayer[jx][jy] = 0;
12411   player->last_jx = jx;
12412   player->last_jy = jy;
12413   player->jx = new_jx;
12414   player->jy = new_jy;
12415   StorePlayer[new_jx][new_jy] = player->element_nr;
12416
12417   if (player->move_delay_value_next != -1)
12418   {
12419     player->move_delay_value = player->move_delay_value_next;
12420     player->move_delay_value_next = -1;
12421   }
12422
12423   player->MovPos =
12424     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12425
12426   player->step_counter++;
12427
12428   PlayerVisit[jx][jy] = FrameCounter;
12429
12430   player->is_moving = TRUE;
12431
12432 #if 1
12433   // should better be called in MovePlayer(), but this breaks some tapes
12434   ScrollPlayer(player, SCROLL_INIT);
12435 #endif
12436
12437   return MP_MOVING;
12438 }
12439
12440 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12441 {
12442   int jx = player->jx, jy = player->jy;
12443   int old_jx = jx, old_jy = jy;
12444   int moved = MP_NO_ACTION;
12445
12446   if (!player->active)
12447     return FALSE;
12448
12449   if (!dx && !dy)
12450   {
12451     if (player->MovPos == 0)
12452     {
12453       player->is_moving = FALSE;
12454       player->is_digging = FALSE;
12455       player->is_collecting = FALSE;
12456       player->is_snapping = FALSE;
12457       player->is_pushing = FALSE;
12458     }
12459
12460     return FALSE;
12461   }
12462
12463   if (player->move_delay > 0)
12464     return FALSE;
12465
12466   player->move_delay = -1;              // set to "uninitialized" value
12467
12468   // store if player is automatically moved to next field
12469   player->is_auto_moving = (player->programmed_action != MV_NONE);
12470
12471   // remove the last programmed player action
12472   player->programmed_action = 0;
12473
12474   if (player->MovPos)
12475   {
12476     // should only happen if pre-1.2 tape recordings are played
12477     // this is only for backward compatibility
12478
12479     int original_move_delay_value = player->move_delay_value;
12480
12481 #if DEBUG
12482     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
12483            tape.counter);
12484 #endif
12485
12486     // scroll remaining steps with finest movement resolution
12487     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12488
12489     while (player->MovPos)
12490     {
12491       ScrollPlayer(player, SCROLL_GO_ON);
12492       ScrollScreen(NULL, SCROLL_GO_ON);
12493
12494       AdvanceFrameAndPlayerCounters(player->index_nr);
12495
12496       DrawAllPlayers();
12497       BackToFront_WithFrameDelay(0);
12498     }
12499
12500     player->move_delay_value = original_move_delay_value;
12501   }
12502
12503   player->is_active = FALSE;
12504
12505   if (player->last_move_dir & MV_HORIZONTAL)
12506   {
12507     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12508       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12509   }
12510   else
12511   {
12512     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12513       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12514   }
12515
12516   if (!moved && !player->is_active)
12517   {
12518     player->is_moving = FALSE;
12519     player->is_digging = FALSE;
12520     player->is_collecting = FALSE;
12521     player->is_snapping = FALSE;
12522     player->is_pushing = FALSE;
12523   }
12524
12525   jx = player->jx;
12526   jy = player->jy;
12527
12528   if (moved & MP_MOVING && !ScreenMovPos &&
12529       (player->index_nr == game.centered_player_nr ||
12530        game.centered_player_nr == -1))
12531   {
12532     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12533
12534     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12535     {
12536       // actual player has left the screen -- scroll in that direction
12537       if (jx != old_jx)         // player has moved horizontally
12538         scroll_x += (jx - old_jx);
12539       else                      // player has moved vertically
12540         scroll_y += (jy - old_jy);
12541     }
12542     else
12543     {
12544       int offset_raw = game.scroll_delay_value;
12545
12546       if (jx != old_jx)         // player has moved horizontally
12547       {
12548         int offset = MIN(offset_raw, (SCR_FIELDX - 2) / 2);
12549         int offset_x = offset * (player->MovDir == MV_LEFT ? +1 : -1);
12550         int new_scroll_x = jx - MIDPOSX + offset_x;
12551
12552         if ((player->MovDir == MV_LEFT  && scroll_x > new_scroll_x) ||
12553             (player->MovDir == MV_RIGHT && scroll_x < new_scroll_x))
12554           scroll_x = new_scroll_x;
12555
12556         // don't scroll over playfield boundaries
12557         scroll_x = MIN(MAX(SBX_Left, scroll_x), SBX_Right);
12558
12559         // don't scroll more than one field at a time
12560         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12561
12562         // don't scroll against the player's moving direction
12563         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12564             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12565           scroll_x = old_scroll_x;
12566       }
12567       else                      // player has moved vertically
12568       {
12569         int offset = MIN(offset_raw, (SCR_FIELDY - 2) / 2);
12570         int offset_y = offset * (player->MovDir == MV_UP ? +1 : -1);
12571         int new_scroll_y = jy - MIDPOSY + offset_y;
12572
12573         if ((player->MovDir == MV_UP   && scroll_y > new_scroll_y) ||
12574             (player->MovDir == MV_DOWN && scroll_y < new_scroll_y))
12575           scroll_y = new_scroll_y;
12576
12577         // don't scroll over playfield boundaries
12578         scroll_y = MIN(MAX(SBY_Upper, scroll_y), SBY_Lower);
12579
12580         // don't scroll more than one field at a time
12581         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12582
12583         // don't scroll against the player's moving direction
12584         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12585             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12586           scroll_y = old_scroll_y;
12587       }
12588     }
12589
12590     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12591     {
12592       if (!network.enabled && game.centered_player_nr == -1 &&
12593           !AllPlayersInVisibleScreen())
12594       {
12595         scroll_x = old_scroll_x;
12596         scroll_y = old_scroll_y;
12597       }
12598       else
12599       {
12600         ScrollScreen(player, SCROLL_INIT);
12601         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12602       }
12603     }
12604   }
12605
12606   player->StepFrame = 0;
12607
12608   if (moved & MP_MOVING)
12609   {
12610     if (old_jx != jx && old_jy == jy)
12611       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12612     else if (old_jx == jx && old_jy != jy)
12613       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12614
12615     TEST_DrawLevelField(jx, jy);        // for "crumbled sand"
12616
12617     player->last_move_dir = player->MovDir;
12618     player->is_moving = TRUE;
12619     player->is_snapping = FALSE;
12620     player->is_switching = FALSE;
12621     player->is_dropping = FALSE;
12622     player->is_dropping_pressed = FALSE;
12623     player->drop_pressed_delay = 0;
12624
12625 #if 0
12626     // should better be called here than above, but this breaks some tapes
12627     ScrollPlayer(player, SCROLL_INIT);
12628 #endif
12629   }
12630   else
12631   {
12632     CheckGravityMovementWhenNotMoving(player);
12633
12634     player->is_moving = FALSE;
12635
12636     /* at this point, the player is allowed to move, but cannot move right now
12637        (e.g. because of something blocking the way) -- ensure that the player
12638        is also allowed to move in the next frame (in old versions before 3.1.1,
12639        the player was forced to wait again for eight frames before next try) */
12640
12641     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12642       player->move_delay = 0;   // allow direct movement in the next frame
12643   }
12644
12645   if (player->move_delay == -1)         // not yet initialized by DigField()
12646     player->move_delay = player->move_delay_value;
12647
12648   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12649   {
12650     TestIfPlayerTouchesBadThing(jx, jy);
12651     TestIfPlayerTouchesCustomElement(jx, jy);
12652   }
12653
12654   if (!player->active)
12655     RemovePlayer(player);
12656
12657   return moved;
12658 }
12659
12660 void ScrollPlayer(struct PlayerInfo *player, int mode)
12661 {
12662   int jx = player->jx, jy = player->jy;
12663   int last_jx = player->last_jx, last_jy = player->last_jy;
12664   int move_stepsize = TILEX / player->move_delay_value;
12665
12666   if (!player->active)
12667     return;
12668
12669   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      // player not moving
12670     return;
12671
12672   if (mode == SCROLL_INIT)
12673   {
12674     player->actual_frame_counter = FrameCounter;
12675     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12676
12677     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12678         Feld[last_jx][last_jy] == EL_EMPTY)
12679     {
12680       int last_field_block_delay = 0;   // start with no blocking at all
12681       int block_delay_adjustment = player->block_delay_adjustment;
12682
12683       // if player blocks last field, add delay for exactly one move
12684       if (player->block_last_field)
12685       {
12686         last_field_block_delay += player->move_delay_value;
12687
12688         // when blocking enabled, prevent moving up despite gravity
12689         if (player->gravity && player->MovDir == MV_UP)
12690           block_delay_adjustment = -1;
12691       }
12692
12693       // add block delay adjustment (also possible when not blocking)
12694       last_field_block_delay += block_delay_adjustment;
12695
12696       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12697       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12698     }
12699
12700     if (player->MovPos != 0)    // player has not yet reached destination
12701       return;
12702   }
12703   else if (!FrameReached(&player->actual_frame_counter, 1))
12704     return;
12705
12706   if (player->MovPos != 0)
12707   {
12708     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12709     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12710
12711     // before DrawPlayer() to draw correct player graphic for this case
12712     if (player->MovPos == 0)
12713       CheckGravityMovement(player);
12714   }
12715
12716   if (player->MovPos == 0)      // player reached destination field
12717   {
12718     if (player->move_delay_reset_counter > 0)
12719     {
12720       player->move_delay_reset_counter--;
12721
12722       if (player->move_delay_reset_counter == 0)
12723       {
12724         // continue with normal speed after quickly moving through gate
12725         HALVE_PLAYER_SPEED(player);
12726
12727         // be able to make the next move without delay
12728         player->move_delay = 0;
12729       }
12730     }
12731
12732     player->last_jx = jx;
12733     player->last_jy = jy;
12734
12735     if (Feld[jx][jy] == EL_EXIT_OPEN ||
12736         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12737         Feld[jx][jy] == EL_EM_EXIT_OPENING ||
12738         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12739         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12740         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12741         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12742         Feld[jx][jy] == EL_SP_EXIT_OPENING)     // <-- special case
12743     {
12744       ExitPlayer(player);
12745
12746       if (game.players_still_needed == 0 &&
12747           (game.friends_still_needed == 0 ||
12748            IS_SP_ELEMENT(Feld[jx][jy])))
12749         LevelSolved();
12750     }
12751
12752     // this breaks one level: "machine", level 000
12753     {
12754       int move_direction = player->MovDir;
12755       int enter_side = MV_DIR_OPPOSITE(move_direction);
12756       int leave_side = move_direction;
12757       int old_jx = last_jx;
12758       int old_jy = last_jy;
12759       int old_element = Feld[old_jx][old_jy];
12760       int new_element = Feld[jx][jy];
12761
12762       if (IS_CUSTOM_ELEMENT(old_element))
12763         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12764                                    CE_LEFT_BY_PLAYER,
12765                                    player->index_bit, leave_side);
12766
12767       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12768                                           CE_PLAYER_LEAVES_X,
12769                                           player->index_bit, leave_side);
12770
12771       if (IS_CUSTOM_ELEMENT(new_element))
12772         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12773                                    player->index_bit, enter_side);
12774
12775       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12776                                           CE_PLAYER_ENTERS_X,
12777                                           player->index_bit, enter_side);
12778
12779       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12780                                         CE_MOVE_OF_X, move_direction);
12781     }
12782
12783     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12784     {
12785       TestIfPlayerTouchesBadThing(jx, jy);
12786       TestIfPlayerTouchesCustomElement(jx, jy);
12787
12788       /* needed because pushed element has not yet reached its destination,
12789          so it would trigger a change event at its previous field location */
12790       if (!player->is_pushing)
12791         TestIfElementTouchesCustomElement(jx, jy);      // for empty space
12792
12793       if (!player->active)
12794         RemovePlayer(player);
12795     }
12796
12797     if (!game.LevelSolved && level.use_step_counter)
12798     {
12799       int i;
12800
12801       TimePlayed++;
12802
12803       if (TimeLeft > 0)
12804       {
12805         TimeLeft--;
12806
12807         if (TimeLeft <= 10 && setup.time_limit)
12808           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12809
12810         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12811
12812         DisplayGameControlValues();
12813
12814         if (!TimeLeft && setup.time_limit)
12815           for (i = 0; i < MAX_PLAYERS; i++)
12816             KillPlayer(&stored_player[i]);
12817       }
12818       else if (game.no_time_limit && !game.all_players_gone)
12819       {
12820         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12821
12822         DisplayGameControlValues();
12823       }
12824     }
12825
12826     if (tape.single_step && tape.recording && !tape.pausing &&
12827         !player->programmed_action)
12828       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12829
12830     if (!player->programmed_action)
12831       CheckSaveEngineSnapshot(player);
12832   }
12833 }
12834
12835 void ScrollScreen(struct PlayerInfo *player, int mode)
12836 {
12837   static unsigned int screen_frame_counter = 0;
12838
12839   if (mode == SCROLL_INIT)
12840   {
12841     // set scrolling step size according to actual player's moving speed
12842     ScrollStepSize = TILEX / player->move_delay_value;
12843
12844     screen_frame_counter = FrameCounter;
12845     ScreenMovDir = player->MovDir;
12846     ScreenMovPos = player->MovPos;
12847     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12848     return;
12849   }
12850   else if (!FrameReached(&screen_frame_counter, 1))
12851     return;
12852
12853   if (ScreenMovPos)
12854   {
12855     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12856     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12857     redraw_mask |= REDRAW_FIELD;
12858   }
12859   else
12860     ScreenMovDir = MV_NONE;
12861 }
12862
12863 void TestIfPlayerTouchesCustomElement(int x, int y)
12864 {
12865   static int xy[4][2] =
12866   {
12867     { 0, -1 },
12868     { -1, 0 },
12869     { +1, 0 },
12870     { 0, +1 }
12871   };
12872   static int trigger_sides[4][2] =
12873   {
12874     // center side       border side
12875     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
12876     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
12877     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
12878     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
12879   };
12880   static int touch_dir[4] =
12881   {
12882     MV_LEFT | MV_RIGHT,
12883     MV_UP   | MV_DOWN,
12884     MV_UP   | MV_DOWN,
12885     MV_LEFT | MV_RIGHT
12886   };
12887   int center_element = Feld[x][y];      // should always be non-moving!
12888   int i;
12889
12890   for (i = 0; i < NUM_DIRECTIONS; i++)
12891   {
12892     int xx = x + xy[i][0];
12893     int yy = y + xy[i][1];
12894     int center_side = trigger_sides[i][0];
12895     int border_side = trigger_sides[i][1];
12896     int border_element;
12897
12898     if (!IN_LEV_FIELD(xx, yy))
12899       continue;
12900
12901     if (IS_PLAYER(x, y))                // player found at center element
12902     {
12903       struct PlayerInfo *player = PLAYERINFO(x, y);
12904
12905       if (game.engine_version < VERSION_IDENT(3,0,7,0))
12906         border_element = Feld[xx][yy];          // may be moving!
12907       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12908         border_element = Feld[xx][yy];
12909       else if (MovDir[xx][yy] & touch_dir[i])   // elements are touching
12910         border_element = MovingOrBlocked2Element(xx, yy);
12911       else
12912         continue;               // center and border element do not touch
12913
12914       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12915                                  player->index_bit, border_side);
12916       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12917                                           CE_PLAYER_TOUCHES_X,
12918                                           player->index_bit, border_side);
12919
12920       {
12921         /* use player element that is initially defined in the level playfield,
12922            not the player element that corresponds to the runtime player number
12923            (example: a level that contains EL_PLAYER_3 as the only player would
12924            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12925         int player_element = PLAYERINFO(x, y)->initial_element;
12926
12927         CheckElementChangeBySide(xx, yy, border_element, player_element,
12928                                  CE_TOUCHING_X, border_side);
12929       }
12930     }
12931     else if (IS_PLAYER(xx, yy))         // player found at border element
12932     {
12933       struct PlayerInfo *player = PLAYERINFO(xx, yy);
12934
12935       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12936       {
12937         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12938           continue;             // center and border element do not touch
12939       }
12940
12941       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12942                                  player->index_bit, center_side);
12943       CheckTriggeredElementChangeByPlayer(x, y, center_element,
12944                                           CE_PLAYER_TOUCHES_X,
12945                                           player->index_bit, center_side);
12946
12947       {
12948         /* use player element that is initially defined in the level playfield,
12949            not the player element that corresponds to the runtime player number
12950            (example: a level that contains EL_PLAYER_3 as the only player would
12951            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12952         int player_element = PLAYERINFO(xx, yy)->initial_element;
12953
12954         CheckElementChangeBySide(x, y, center_element, player_element,
12955                                  CE_TOUCHING_X, center_side);
12956       }
12957
12958       break;
12959     }
12960   }
12961 }
12962
12963 void TestIfElementTouchesCustomElement(int x, int y)
12964 {
12965   static int xy[4][2] =
12966   {
12967     { 0, -1 },
12968     { -1, 0 },
12969     { +1, 0 },
12970     { 0, +1 }
12971   };
12972   static int trigger_sides[4][2] =
12973   {
12974     // center side      border side
12975     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
12976     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
12977     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
12978     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
12979   };
12980   static int touch_dir[4] =
12981   {
12982     MV_LEFT | MV_RIGHT,
12983     MV_UP   | MV_DOWN,
12984     MV_UP   | MV_DOWN,
12985     MV_LEFT | MV_RIGHT
12986   };
12987   boolean change_center_element = FALSE;
12988   int center_element = Feld[x][y];      // should always be non-moving!
12989   int border_element_old[NUM_DIRECTIONS];
12990   int i;
12991
12992   for (i = 0; i < NUM_DIRECTIONS; i++)
12993   {
12994     int xx = x + xy[i][0];
12995     int yy = y + xy[i][1];
12996     int border_element;
12997
12998     border_element_old[i] = -1;
12999
13000     if (!IN_LEV_FIELD(xx, yy))
13001       continue;
13002
13003     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13004       border_element = Feld[xx][yy];    // may be moving!
13005     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13006       border_element = Feld[xx][yy];
13007     else if (MovDir[xx][yy] & touch_dir[i])     // elements are touching
13008       border_element = MovingOrBlocked2Element(xx, yy);
13009     else
13010       continue;                 // center and border element do not touch
13011
13012     border_element_old[i] = border_element;
13013   }
13014
13015   for (i = 0; i < NUM_DIRECTIONS; i++)
13016   {
13017     int xx = x + xy[i][0];
13018     int yy = y + xy[i][1];
13019     int center_side = trigger_sides[i][0];
13020     int border_element = border_element_old[i];
13021
13022     if (border_element == -1)
13023       continue;
13024
13025     // check for change of border element
13026     CheckElementChangeBySide(xx, yy, border_element, center_element,
13027                              CE_TOUCHING_X, center_side);
13028
13029     // (center element cannot be player, so we dont have to check this here)
13030   }
13031
13032   for (i = 0; i < NUM_DIRECTIONS; i++)
13033   {
13034     int xx = x + xy[i][0];
13035     int yy = y + xy[i][1];
13036     int border_side = trigger_sides[i][1];
13037     int border_element = border_element_old[i];
13038
13039     if (border_element == -1)
13040       continue;
13041
13042     // check for change of center element (but change it only once)
13043     if (!change_center_element)
13044       change_center_element =
13045         CheckElementChangeBySide(x, y, center_element, border_element,
13046                                  CE_TOUCHING_X, border_side);
13047
13048     if (IS_PLAYER(xx, yy))
13049     {
13050       /* use player element that is initially defined in the level playfield,
13051          not the player element that corresponds to the runtime player number
13052          (example: a level that contains EL_PLAYER_3 as the only player would
13053          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13054       int player_element = PLAYERINFO(xx, yy)->initial_element;
13055
13056       CheckElementChangeBySide(x, y, center_element, player_element,
13057                                CE_TOUCHING_X, border_side);
13058     }
13059   }
13060 }
13061
13062 void TestIfElementHitsCustomElement(int x, int y, int direction)
13063 {
13064   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13065   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13066   int hitx = x + dx, hity = y + dy;
13067   int hitting_element = Feld[x][y];
13068   int touched_element;
13069
13070   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13071     return;
13072
13073   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13074                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13075
13076   if (IN_LEV_FIELD(hitx, hity))
13077   {
13078     int opposite_direction = MV_DIR_OPPOSITE(direction);
13079     int hitting_side = direction;
13080     int touched_side = opposite_direction;
13081     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13082                           MovDir[hitx][hity] != direction ||
13083                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13084
13085     object_hit = TRUE;
13086
13087     if (object_hit)
13088     {
13089       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13090                                CE_HITTING_X, touched_side);
13091
13092       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13093                                CE_HIT_BY_X, hitting_side);
13094
13095       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13096                                CE_HIT_BY_SOMETHING, opposite_direction);
13097
13098       if (IS_PLAYER(hitx, hity))
13099       {
13100         /* use player element that is initially defined in the level playfield,
13101            not the player element that corresponds to the runtime player number
13102            (example: a level that contains EL_PLAYER_3 as the only player would
13103            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13104         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13105
13106         CheckElementChangeBySide(x, y, hitting_element, player_element,
13107                                  CE_HITTING_X, touched_side);
13108       }
13109     }
13110   }
13111
13112   // "hitting something" is also true when hitting the playfield border
13113   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13114                            CE_HITTING_SOMETHING, direction);
13115 }
13116
13117 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13118 {
13119   int i, kill_x = -1, kill_y = -1;
13120
13121   int bad_element = -1;
13122   static int test_xy[4][2] =
13123   {
13124     { 0, -1 },
13125     { -1, 0 },
13126     { +1, 0 },
13127     { 0, +1 }
13128   };
13129   static int test_dir[4] =
13130   {
13131     MV_UP,
13132     MV_LEFT,
13133     MV_RIGHT,
13134     MV_DOWN
13135   };
13136
13137   for (i = 0; i < NUM_DIRECTIONS; i++)
13138   {
13139     int test_x, test_y, test_move_dir, test_element;
13140
13141     test_x = good_x + test_xy[i][0];
13142     test_y = good_y + test_xy[i][1];
13143
13144     if (!IN_LEV_FIELD(test_x, test_y))
13145       continue;
13146
13147     test_move_dir =
13148       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13149
13150     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13151
13152     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13153        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13154     */
13155     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13156         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13157     {
13158       kill_x = test_x;
13159       kill_y = test_y;
13160       bad_element = test_element;
13161
13162       break;
13163     }
13164   }
13165
13166   if (kill_x != -1 || kill_y != -1)
13167   {
13168     if (IS_PLAYER(good_x, good_y))
13169     {
13170       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13171
13172       if (player->shield_deadly_time_left > 0 &&
13173           !IS_INDESTRUCTIBLE(bad_element))
13174         Bang(kill_x, kill_y);
13175       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13176         KillPlayer(player);
13177     }
13178     else
13179       Bang(good_x, good_y);
13180   }
13181 }
13182
13183 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13184 {
13185   int i, kill_x = -1, kill_y = -1;
13186   int bad_element = Feld[bad_x][bad_y];
13187   static int test_xy[4][2] =
13188   {
13189     { 0, -1 },
13190     { -1, 0 },
13191     { +1, 0 },
13192     { 0, +1 }
13193   };
13194   static int touch_dir[4] =
13195   {
13196     MV_LEFT | MV_RIGHT,
13197     MV_UP   | MV_DOWN,
13198     MV_UP   | MV_DOWN,
13199     MV_LEFT | MV_RIGHT
13200   };
13201   static int test_dir[4] =
13202   {
13203     MV_UP,
13204     MV_LEFT,
13205     MV_RIGHT,
13206     MV_DOWN
13207   };
13208
13209   if (bad_element == EL_EXPLOSION)      // skip just exploding bad things
13210     return;
13211
13212   for (i = 0; i < NUM_DIRECTIONS; i++)
13213   {
13214     int test_x, test_y, test_move_dir, test_element;
13215
13216     test_x = bad_x + test_xy[i][0];
13217     test_y = bad_y + test_xy[i][1];
13218
13219     if (!IN_LEV_FIELD(test_x, test_y))
13220       continue;
13221
13222     test_move_dir =
13223       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13224
13225     test_element = Feld[test_x][test_y];
13226
13227     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13228        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13229     */
13230     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13231         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13232     {
13233       // good thing is player or penguin that does not move away
13234       if (IS_PLAYER(test_x, test_y))
13235       {
13236         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13237
13238         if (bad_element == EL_ROBOT && player->is_moving)
13239           continue;     // robot does not kill player if he is moving
13240
13241         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13242         {
13243           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13244             continue;           // center and border element do not touch
13245         }
13246
13247         kill_x = test_x;
13248         kill_y = test_y;
13249
13250         break;
13251       }
13252       else if (test_element == EL_PENGUIN)
13253       {
13254         kill_x = test_x;
13255         kill_y = test_y;
13256
13257         break;
13258       }
13259     }
13260   }
13261
13262   if (kill_x != -1 || kill_y != -1)
13263   {
13264     if (IS_PLAYER(kill_x, kill_y))
13265     {
13266       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13267
13268       if (player->shield_deadly_time_left > 0 &&
13269           !IS_INDESTRUCTIBLE(bad_element))
13270         Bang(bad_x, bad_y);
13271       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13272         KillPlayer(player);
13273     }
13274     else
13275       Bang(kill_x, kill_y);
13276   }
13277 }
13278
13279 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13280 {
13281   int bad_element = Feld[bad_x][bad_y];
13282   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13283   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13284   int test_x = bad_x + dx, test_y = bad_y + dy;
13285   int test_move_dir, test_element;
13286   int kill_x = -1, kill_y = -1;
13287
13288   if (!IN_LEV_FIELD(test_x, test_y))
13289     return;
13290
13291   test_move_dir =
13292     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13293
13294   test_element = Feld[test_x][test_y];
13295
13296   if (test_move_dir != bad_move_dir)
13297   {
13298     // good thing can be player or penguin that does not move away
13299     if (IS_PLAYER(test_x, test_y))
13300     {
13301       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13302
13303       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13304          player as being hit when he is moving towards the bad thing, because
13305          the "get hit by" condition would be lost after the player stops) */
13306       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13307         return;         // player moves away from bad thing
13308
13309       kill_x = test_x;
13310       kill_y = test_y;
13311     }
13312     else if (test_element == EL_PENGUIN)
13313     {
13314       kill_x = test_x;
13315       kill_y = test_y;
13316     }
13317   }
13318
13319   if (kill_x != -1 || kill_y != -1)
13320   {
13321     if (IS_PLAYER(kill_x, kill_y))
13322     {
13323       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13324
13325       if (player->shield_deadly_time_left > 0 &&
13326           !IS_INDESTRUCTIBLE(bad_element))
13327         Bang(bad_x, bad_y);
13328       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13329         KillPlayer(player);
13330     }
13331     else
13332       Bang(kill_x, kill_y);
13333   }
13334 }
13335
13336 void TestIfPlayerTouchesBadThing(int x, int y)
13337 {
13338   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13339 }
13340
13341 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13342 {
13343   TestIfGoodThingHitsBadThing(x, y, move_dir);
13344 }
13345
13346 void TestIfBadThingTouchesPlayer(int x, int y)
13347 {
13348   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13349 }
13350
13351 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13352 {
13353   TestIfBadThingHitsGoodThing(x, y, move_dir);
13354 }
13355
13356 void TestIfFriendTouchesBadThing(int x, int y)
13357 {
13358   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13359 }
13360
13361 void TestIfBadThingTouchesFriend(int x, int y)
13362 {
13363   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13364 }
13365
13366 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13367 {
13368   int i, kill_x = bad_x, kill_y = bad_y;
13369   static int xy[4][2] =
13370   {
13371     { 0, -1 },
13372     { -1, 0 },
13373     { +1, 0 },
13374     { 0, +1 }
13375   };
13376
13377   for (i = 0; i < NUM_DIRECTIONS; i++)
13378   {
13379     int x, y, element;
13380
13381     x = bad_x + xy[i][0];
13382     y = bad_y + xy[i][1];
13383     if (!IN_LEV_FIELD(x, y))
13384       continue;
13385
13386     element = Feld[x][y];
13387     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13388         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13389     {
13390       kill_x = x;
13391       kill_y = y;
13392       break;
13393     }
13394   }
13395
13396   if (kill_x != bad_x || kill_y != bad_y)
13397     Bang(bad_x, bad_y);
13398 }
13399
13400 void KillPlayer(struct PlayerInfo *player)
13401 {
13402   int jx = player->jx, jy = player->jy;
13403
13404   if (!player->active)
13405     return;
13406
13407 #if 0
13408   printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
13409          player->killed, player->active, player->reanimated);
13410 #endif
13411
13412   /* the following code was introduced to prevent an infinite loop when calling
13413      -> Bang()
13414      -> CheckTriggeredElementChangeExt()
13415      -> ExecuteCustomElementAction()
13416      -> KillPlayer()
13417      -> (infinitely repeating the above sequence of function calls)
13418      which occurs when killing the player while having a CE with the setting
13419      "kill player X when explosion of <player X>"; the solution using a new
13420      field "player->killed" was chosen for backwards compatibility, although
13421      clever use of the fields "player->active" etc. would probably also work */
13422 #if 1
13423   if (player->killed)
13424     return;
13425 #endif
13426
13427   player->killed = TRUE;
13428
13429   // remove accessible field at the player's position
13430   Feld[jx][jy] = EL_EMPTY;
13431
13432   // deactivate shield (else Bang()/Explode() would not work right)
13433   player->shield_normal_time_left = 0;
13434   player->shield_deadly_time_left = 0;
13435
13436 #if 0
13437   printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
13438          player->killed, player->active, player->reanimated);
13439 #endif
13440
13441   Bang(jx, jy);
13442
13443 #if 0
13444   printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
13445          player->killed, player->active, player->reanimated);
13446 #endif
13447
13448   if (player->reanimated)       // killed player may have been reanimated
13449     player->killed = player->reanimated = FALSE;
13450   else
13451     BuryPlayer(player);
13452 }
13453
13454 static void KillPlayerUnlessEnemyProtected(int x, int y)
13455 {
13456   if (!PLAYER_ENEMY_PROTECTED(x, y))
13457     KillPlayer(PLAYERINFO(x, y));
13458 }
13459
13460 static void KillPlayerUnlessExplosionProtected(int x, int y)
13461 {
13462   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13463     KillPlayer(PLAYERINFO(x, y));
13464 }
13465
13466 void BuryPlayer(struct PlayerInfo *player)
13467 {
13468   int jx = player->jx, jy = player->jy;
13469
13470   if (!player->active)
13471     return;
13472
13473   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13474   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13475
13476   RemovePlayer(player);
13477
13478   player->buried = TRUE;
13479
13480   if (game.all_players_gone)
13481     game.GameOver = TRUE;
13482 }
13483
13484 void RemovePlayer(struct PlayerInfo *player)
13485 {
13486   int jx = player->jx, jy = player->jy;
13487   int i, found = FALSE;
13488
13489   player->present = FALSE;
13490   player->active = FALSE;
13491
13492   // required for some CE actions (even if the player is not active anymore)
13493   player->MovPos = 0;
13494
13495   if (!ExplodeField[jx][jy])
13496     StorePlayer[jx][jy] = 0;
13497
13498   if (player->is_moving)
13499     TEST_DrawLevelField(player->last_jx, player->last_jy);
13500
13501   for (i = 0; i < MAX_PLAYERS; i++)
13502     if (stored_player[i].active)
13503       found = TRUE;
13504
13505   if (!found)
13506   {
13507     game.all_players_gone = TRUE;
13508     game.GameOver = TRUE;
13509   }
13510
13511   game.exit_x = game.robot_wheel_x = jx;
13512   game.exit_y = game.robot_wheel_y = jy;
13513 }
13514
13515 void ExitPlayer(struct PlayerInfo *player)
13516 {
13517   DrawPlayer(player);   // needed here only to cleanup last field
13518   RemovePlayer(player);
13519
13520   if (game.players_still_needed > 0)
13521     game.players_still_needed--;
13522 }
13523
13524 static void setFieldForSnapping(int x, int y, int element, int direction)
13525 {
13526   struct ElementInfo *ei = &element_info[element];
13527   int direction_bit = MV_DIR_TO_BIT(direction);
13528   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13529   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13530                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13531
13532   Feld[x][y] = EL_ELEMENT_SNAPPING;
13533   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13534
13535   ResetGfxAnimation(x, y);
13536
13537   GfxElement[x][y] = element;
13538   GfxAction[x][y] = action;
13539   GfxDir[x][y] = direction;
13540   GfxFrame[x][y] = -1;
13541 }
13542
13543 /*
13544   =============================================================================
13545   checkDiagonalPushing()
13546   -----------------------------------------------------------------------------
13547   check if diagonal input device direction results in pushing of object
13548   (by checking if the alternative direction is walkable, diggable, ...)
13549   =============================================================================
13550 */
13551
13552 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13553                                     int x, int y, int real_dx, int real_dy)
13554 {
13555   int jx, jy, dx, dy, xx, yy;
13556
13557   if (real_dx == 0 || real_dy == 0)     // no diagonal direction => push
13558     return TRUE;
13559
13560   // diagonal direction: check alternative direction
13561   jx = player->jx;
13562   jy = player->jy;
13563   dx = x - jx;
13564   dy = y - jy;
13565   xx = jx + (dx == 0 ? real_dx : 0);
13566   yy = jy + (dy == 0 ? real_dy : 0);
13567
13568   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13569 }
13570
13571 /*
13572   =============================================================================
13573   DigField()
13574   -----------------------------------------------------------------------------
13575   x, y:                 field next to player (non-diagonal) to try to dig to
13576   real_dx, real_dy:     direction as read from input device (can be diagonal)
13577   =============================================================================
13578 */
13579
13580 static int DigField(struct PlayerInfo *player,
13581                     int oldx, int oldy, int x, int y,
13582                     int real_dx, int real_dy, int mode)
13583 {
13584   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13585   boolean player_was_pushing = player->is_pushing;
13586   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13587   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13588   int jx = oldx, jy = oldy;
13589   int dx = x - jx, dy = y - jy;
13590   int nextx = x + dx, nexty = y + dy;
13591   int move_direction = (dx == -1 ? MV_LEFT  :
13592                         dx == +1 ? MV_RIGHT :
13593                         dy == -1 ? MV_UP    :
13594                         dy == +1 ? MV_DOWN  : MV_NONE);
13595   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13596   int dig_side = MV_DIR_OPPOSITE(move_direction);
13597   int old_element = Feld[jx][jy];
13598   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13599   int collect_count;
13600
13601   if (is_player)                // function can also be called by EL_PENGUIN
13602   {
13603     if (player->MovPos == 0)
13604     {
13605       player->is_digging = FALSE;
13606       player->is_collecting = FALSE;
13607     }
13608
13609     if (player->MovPos == 0)    // last pushing move finished
13610       player->is_pushing = FALSE;
13611
13612     if (mode == DF_NO_PUSH)     // player just stopped pushing
13613     {
13614       player->is_switching = FALSE;
13615       player->push_delay = -1;
13616
13617       return MP_NO_ACTION;
13618     }
13619   }
13620
13621   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13622     old_element = Back[jx][jy];
13623
13624   // in case of element dropped at player position, check background
13625   else if (Back[jx][jy] != EL_EMPTY &&
13626            game.engine_version >= VERSION_IDENT(2,2,0,0))
13627     old_element = Back[jx][jy];
13628
13629   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13630     return MP_NO_ACTION;        // field has no opening in this direction
13631
13632   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13633     return MP_NO_ACTION;        // field has no opening in this direction
13634
13635   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13636   {
13637     SplashAcid(x, y);
13638
13639     Feld[jx][jy] = player->artwork_element;
13640     InitMovingField(jx, jy, MV_DOWN);
13641     Store[jx][jy] = EL_ACID;
13642     ContinueMoving(jx, jy);
13643     BuryPlayer(player);
13644
13645     return MP_DONT_RUN_INTO;
13646   }
13647
13648   if (player_can_move && DONT_RUN_INTO(element))
13649   {
13650     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13651
13652     return MP_DONT_RUN_INTO;
13653   }
13654
13655   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13656     return MP_NO_ACTION;
13657
13658   collect_count = element_info[element].collect_count_initial;
13659
13660   if (!is_player && !IS_COLLECTIBLE(element))   // penguin cannot collect it
13661     return MP_NO_ACTION;
13662
13663   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13664     player_can_move = player_can_move_or_snap;
13665
13666   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13667       game.engine_version >= VERSION_IDENT(2,2,0,0))
13668   {
13669     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13670                                player->index_bit, dig_side);
13671     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13672                                         player->index_bit, dig_side);
13673
13674     if (element == EL_DC_LANDMINE)
13675       Bang(x, y);
13676
13677     if (Feld[x][y] != element)          // field changed by snapping
13678       return MP_ACTION;
13679
13680     return MP_NO_ACTION;
13681   }
13682
13683   if (player->gravity && is_player && !player->is_auto_moving &&
13684       canFallDown(player) && move_direction != MV_DOWN &&
13685       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13686     return MP_NO_ACTION;        // player cannot walk here due to gravity
13687
13688   if (player_can_move &&
13689       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13690   {
13691     int sound_element = SND_ELEMENT(element);
13692     int sound_action = ACTION_WALKING;
13693
13694     if (IS_RND_GATE(element))
13695     {
13696       if (!player->key[RND_GATE_NR(element)])
13697         return MP_NO_ACTION;
13698     }
13699     else if (IS_RND_GATE_GRAY(element))
13700     {
13701       if (!player->key[RND_GATE_GRAY_NR(element)])
13702         return MP_NO_ACTION;
13703     }
13704     else if (IS_RND_GATE_GRAY_ACTIVE(element))
13705     {
13706       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13707         return MP_NO_ACTION;
13708     }
13709     else if (element == EL_EXIT_OPEN ||
13710              element == EL_EM_EXIT_OPEN ||
13711              element == EL_EM_EXIT_OPENING ||
13712              element == EL_STEEL_EXIT_OPEN ||
13713              element == EL_EM_STEEL_EXIT_OPEN ||
13714              element == EL_EM_STEEL_EXIT_OPENING ||
13715              element == EL_SP_EXIT_OPEN ||
13716              element == EL_SP_EXIT_OPENING)
13717     {
13718       sound_action = ACTION_PASSING;    // player is passing exit
13719     }
13720     else if (element == EL_EMPTY)
13721     {
13722       sound_action = ACTION_MOVING;             // nothing to walk on
13723     }
13724
13725     // play sound from background or player, whatever is available
13726     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13727       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13728     else
13729       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13730   }
13731   else if (player_can_move &&
13732            IS_PASSABLE(element) && canPassField(x, y, move_direction))
13733   {
13734     if (!ACCESS_FROM(element, opposite_direction))
13735       return MP_NO_ACTION;      // field not accessible from this direction
13736
13737     if (CAN_MOVE(element))      // only fixed elements can be passed!
13738       return MP_NO_ACTION;
13739
13740     if (IS_EM_GATE(element))
13741     {
13742       if (!player->key[EM_GATE_NR(element)])
13743         return MP_NO_ACTION;
13744     }
13745     else if (IS_EM_GATE_GRAY(element))
13746     {
13747       if (!player->key[EM_GATE_GRAY_NR(element)])
13748         return MP_NO_ACTION;
13749     }
13750     else if (IS_EM_GATE_GRAY_ACTIVE(element))
13751     {
13752       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13753         return MP_NO_ACTION;
13754     }
13755     else if (IS_EMC_GATE(element))
13756     {
13757       if (!player->key[EMC_GATE_NR(element)])
13758         return MP_NO_ACTION;
13759     }
13760     else if (IS_EMC_GATE_GRAY(element))
13761     {
13762       if (!player->key[EMC_GATE_GRAY_NR(element)])
13763         return MP_NO_ACTION;
13764     }
13765     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13766     {
13767       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13768         return MP_NO_ACTION;
13769     }
13770     else if (element == EL_DC_GATE_WHITE ||
13771              element == EL_DC_GATE_WHITE_GRAY ||
13772              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13773     {
13774       if (player->num_white_keys == 0)
13775         return MP_NO_ACTION;
13776
13777       player->num_white_keys--;
13778     }
13779     else if (IS_SP_PORT(element))
13780     {
13781       if (element == EL_SP_GRAVITY_PORT_LEFT ||
13782           element == EL_SP_GRAVITY_PORT_RIGHT ||
13783           element == EL_SP_GRAVITY_PORT_UP ||
13784           element == EL_SP_GRAVITY_PORT_DOWN)
13785         player->gravity = !player->gravity;
13786       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13787                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13788                element == EL_SP_GRAVITY_ON_PORT_UP ||
13789                element == EL_SP_GRAVITY_ON_PORT_DOWN)
13790         player->gravity = TRUE;
13791       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13792                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13793                element == EL_SP_GRAVITY_OFF_PORT_UP ||
13794                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13795         player->gravity = FALSE;
13796     }
13797
13798     // automatically move to the next field with double speed
13799     player->programmed_action = move_direction;
13800
13801     if (player->move_delay_reset_counter == 0)
13802     {
13803       player->move_delay_reset_counter = 2;     // two double speed steps
13804
13805       DOUBLE_PLAYER_SPEED(player);
13806     }
13807
13808     PlayLevelSoundAction(x, y, ACTION_PASSING);
13809   }
13810   else if (player_can_move_or_snap && IS_DIGGABLE(element))
13811   {
13812     RemoveField(x, y);
13813
13814     if (mode != DF_SNAP)
13815     {
13816       GfxElement[x][y] = GFX_ELEMENT(element);
13817       player->is_digging = TRUE;
13818     }
13819
13820     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13821
13822     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13823                                         player->index_bit, dig_side);
13824
13825     if (mode == DF_SNAP)
13826     {
13827       if (level.block_snap_field)
13828         setFieldForSnapping(x, y, element, move_direction);
13829       else
13830         TestIfElementTouchesCustomElement(x, y);        // for empty space
13831
13832       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13833                                           player->index_bit, dig_side);
13834     }
13835   }
13836   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13837   {
13838     RemoveField(x, y);
13839
13840     if (is_player && mode != DF_SNAP)
13841     {
13842       GfxElement[x][y] = element;
13843       player->is_collecting = TRUE;
13844     }
13845
13846     if (element == EL_SPEED_PILL)
13847     {
13848       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13849     }
13850     else if (element == EL_EXTRA_TIME && level.time > 0)
13851     {
13852       TimeLeft += level.extra_time;
13853
13854       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13855
13856       DisplayGameControlValues();
13857     }
13858     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13859     {
13860       player->shield_normal_time_left += level.shield_normal_time;
13861       if (element == EL_SHIELD_DEADLY)
13862         player->shield_deadly_time_left += level.shield_deadly_time;
13863     }
13864     else if (element == EL_DYNAMITE ||
13865              element == EL_EM_DYNAMITE ||
13866              element == EL_SP_DISK_RED)
13867     {
13868       if (player->inventory_size < MAX_INVENTORY_SIZE)
13869         player->inventory_element[player->inventory_size++] = element;
13870
13871       DrawGameDoorValues();
13872     }
13873     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13874     {
13875       player->dynabomb_count++;
13876       player->dynabombs_left++;
13877     }
13878     else if (element == EL_DYNABOMB_INCREASE_SIZE)
13879     {
13880       player->dynabomb_size++;
13881     }
13882     else if (element == EL_DYNABOMB_INCREASE_POWER)
13883     {
13884       player->dynabomb_xl = TRUE;
13885     }
13886     else if (IS_KEY(element))
13887     {
13888       player->key[KEY_NR(element)] = TRUE;
13889
13890       DrawGameDoorValues();
13891     }
13892     else if (element == EL_DC_KEY_WHITE)
13893     {
13894       player->num_white_keys++;
13895
13896       // display white keys?
13897       // DrawGameDoorValues();
13898     }
13899     else if (IS_ENVELOPE(element))
13900     {
13901       player->show_envelope = element;
13902     }
13903     else if (element == EL_EMC_LENSES)
13904     {
13905       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13906
13907       RedrawAllInvisibleElementsForLenses();
13908     }
13909     else if (element == EL_EMC_MAGNIFIER)
13910     {
13911       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13912
13913       RedrawAllInvisibleElementsForMagnifier();
13914     }
13915     else if (IS_DROPPABLE(element) ||
13916              IS_THROWABLE(element))     // can be collected and dropped
13917     {
13918       int i;
13919
13920       if (collect_count == 0)
13921         player->inventory_infinite_element = element;
13922       else
13923         for (i = 0; i < collect_count; i++)
13924           if (player->inventory_size < MAX_INVENTORY_SIZE)
13925             player->inventory_element[player->inventory_size++] = element;
13926
13927       DrawGameDoorValues();
13928     }
13929     else if (collect_count > 0)
13930     {
13931       game.gems_still_needed -= collect_count;
13932       if (game.gems_still_needed < 0)
13933         game.gems_still_needed = 0;
13934
13935       game.snapshot.collected_item = TRUE;
13936
13937       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
13938
13939       DisplayGameControlValues();
13940     }
13941
13942     RaiseScoreElement(element);
13943     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13944
13945     if (is_player)
13946       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13947                                           player->index_bit, dig_side);
13948
13949     if (mode == DF_SNAP)
13950     {
13951       if (level.block_snap_field)
13952         setFieldForSnapping(x, y, element, move_direction);
13953       else
13954         TestIfElementTouchesCustomElement(x, y);        // for empty space
13955
13956       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13957                                           player->index_bit, dig_side);
13958     }
13959   }
13960   else if (player_can_move_or_snap && IS_PUSHABLE(element))
13961   {
13962     if (mode == DF_SNAP && element != EL_BD_ROCK)
13963       return MP_NO_ACTION;
13964
13965     if (CAN_FALL(element) && dy)
13966       return MP_NO_ACTION;
13967
13968     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13969         !(element == EL_SPRING && level.use_spring_bug))
13970       return MP_NO_ACTION;
13971
13972     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13973         ((move_direction & MV_VERTICAL &&
13974           ((element_info[element].move_pattern & MV_LEFT &&
13975             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13976            (element_info[element].move_pattern & MV_RIGHT &&
13977             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13978          (move_direction & MV_HORIZONTAL &&
13979           ((element_info[element].move_pattern & MV_UP &&
13980             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13981            (element_info[element].move_pattern & MV_DOWN &&
13982             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13983       return MP_NO_ACTION;
13984
13985     // do not push elements already moving away faster than player
13986     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13987         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13988       return MP_NO_ACTION;
13989
13990     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13991     {
13992       if (player->push_delay_value == -1 || !player_was_pushing)
13993         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13994     }
13995     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13996     {
13997       if (player->push_delay_value == -1)
13998         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13999     }
14000     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14001     {
14002       if (!player->is_pushing)
14003         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14004     }
14005
14006     player->is_pushing = TRUE;
14007     player->is_active = TRUE;
14008
14009     if (!(IN_LEV_FIELD(nextx, nexty) &&
14010           (IS_FREE(nextx, nexty) ||
14011            (IS_SB_ELEMENT(element) &&
14012             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14013            (IS_CUSTOM_ELEMENT(element) &&
14014             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
14015       return MP_NO_ACTION;
14016
14017     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14018       return MP_NO_ACTION;
14019
14020     if (player->push_delay == -1)       // new pushing; restart delay
14021       player->push_delay = 0;
14022
14023     if (player->push_delay < player->push_delay_value &&
14024         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14025         element != EL_SPRING && element != EL_BALLOON)
14026     {
14027       // make sure that there is no move delay before next try to push
14028       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14029         player->move_delay = 0;
14030
14031       return MP_NO_ACTION;
14032     }
14033
14034     if (IS_CUSTOM_ELEMENT(element) &&
14035         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14036     {
14037       if (!DigFieldByCE(nextx, nexty, element))
14038         return MP_NO_ACTION;
14039     }
14040
14041     if (IS_SB_ELEMENT(element))
14042     {
14043       boolean sokoban_task_solved = FALSE;
14044
14045       if (element == EL_SOKOBAN_FIELD_FULL)
14046       {
14047         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14048
14049         IncrementSokobanFieldsNeeded();
14050         IncrementSokobanObjectsNeeded();
14051       }
14052
14053       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14054       {
14055         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14056
14057         DecrementSokobanFieldsNeeded();
14058         DecrementSokobanObjectsNeeded();
14059
14060         // sokoban object was pushed from empty field to sokoban field
14061         if (Back[x][y] == EL_EMPTY)
14062           sokoban_task_solved = TRUE;
14063       }
14064
14065       Feld[x][y] = EL_SOKOBAN_OBJECT;
14066
14067       if (Back[x][y] == Back[nextx][nexty])
14068         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14069       else if (Back[x][y] != 0)
14070         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14071                                     ACTION_EMPTYING);
14072       else
14073         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14074                                     ACTION_FILLING);
14075
14076       if (sokoban_task_solved &&
14077           game.sokoban_fields_still_needed == 0 &&
14078           game.sokoban_objects_still_needed == 0 &&
14079           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
14080       {
14081         game.players_still_needed = 0;
14082
14083         LevelSolved();
14084
14085         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14086       }
14087     }
14088     else
14089       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14090
14091     InitMovingField(x, y, move_direction);
14092     GfxAction[x][y] = ACTION_PUSHING;
14093
14094     if (mode == DF_SNAP)
14095       ContinueMoving(x, y);
14096     else
14097       MovPos[x][y] = (dx != 0 ? dx : dy);
14098
14099     Pushed[x][y] = TRUE;
14100     Pushed[nextx][nexty] = TRUE;
14101
14102     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14103       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14104     else
14105       player->push_delay_value = -1;    // get new value later
14106
14107     // check for element change _after_ element has been pushed
14108     if (game.use_change_when_pushing_bug)
14109     {
14110       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14111                                  player->index_bit, dig_side);
14112       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14113                                           player->index_bit, dig_side);
14114     }
14115   }
14116   else if (IS_SWITCHABLE(element))
14117   {
14118     if (PLAYER_SWITCHING(player, x, y))
14119     {
14120       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14121                                           player->index_bit, dig_side);
14122
14123       return MP_ACTION;
14124     }
14125
14126     player->is_switching = TRUE;
14127     player->switch_x = x;
14128     player->switch_y = y;
14129
14130     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14131
14132     if (element == EL_ROBOT_WHEEL)
14133     {
14134       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14135
14136       game.robot_wheel_x = x;
14137       game.robot_wheel_y = y;
14138       game.robot_wheel_active = TRUE;
14139
14140       TEST_DrawLevelField(x, y);
14141     }
14142     else if (element == EL_SP_TERMINAL)
14143     {
14144       int xx, yy;
14145
14146       SCAN_PLAYFIELD(xx, yy)
14147       {
14148         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
14149         {
14150           Bang(xx, yy);
14151         }
14152         else if (Feld[xx][yy] == EL_SP_TERMINAL)
14153         {
14154           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14155
14156           ResetGfxAnimation(xx, yy);
14157           TEST_DrawLevelField(xx, yy);
14158         }
14159       }
14160     }
14161     else if (IS_BELT_SWITCH(element))
14162     {
14163       ToggleBeltSwitch(x, y);
14164     }
14165     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14166              element == EL_SWITCHGATE_SWITCH_DOWN ||
14167              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14168              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14169     {
14170       ToggleSwitchgateSwitch(x, y);
14171     }
14172     else if (element == EL_LIGHT_SWITCH ||
14173              element == EL_LIGHT_SWITCH_ACTIVE)
14174     {
14175       ToggleLightSwitch(x, y);
14176     }
14177     else if (element == EL_TIMEGATE_SWITCH ||
14178              element == EL_DC_TIMEGATE_SWITCH)
14179     {
14180       ActivateTimegateSwitch(x, y);
14181     }
14182     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14183              element == EL_BALLOON_SWITCH_RIGHT ||
14184              element == EL_BALLOON_SWITCH_UP    ||
14185              element == EL_BALLOON_SWITCH_DOWN  ||
14186              element == EL_BALLOON_SWITCH_NONE  ||
14187              element == EL_BALLOON_SWITCH_ANY)
14188     {
14189       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14190                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14191                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14192                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14193                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14194                              move_direction);
14195     }
14196     else if (element == EL_LAMP)
14197     {
14198       Feld[x][y] = EL_LAMP_ACTIVE;
14199       game.lights_still_needed--;
14200
14201       ResetGfxAnimation(x, y);
14202       TEST_DrawLevelField(x, y);
14203     }
14204     else if (element == EL_TIME_ORB_FULL)
14205     {
14206       Feld[x][y] = EL_TIME_ORB_EMPTY;
14207
14208       if (level.time > 0 || level.use_time_orb_bug)
14209       {
14210         TimeLeft += level.time_orb_time;
14211         game.no_time_limit = FALSE;
14212
14213         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14214
14215         DisplayGameControlValues();
14216       }
14217
14218       ResetGfxAnimation(x, y);
14219       TEST_DrawLevelField(x, y);
14220     }
14221     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14222              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14223     {
14224       int xx, yy;
14225
14226       game.ball_state = !game.ball_state;
14227
14228       SCAN_PLAYFIELD(xx, yy)
14229       {
14230         int e = Feld[xx][yy];
14231
14232         if (game.ball_state)
14233         {
14234           if (e == EL_EMC_MAGIC_BALL)
14235             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14236           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14237             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14238         }
14239         else
14240         {
14241           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14242             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14243           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14244             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14245         }
14246       }
14247     }
14248
14249     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14250                                         player->index_bit, dig_side);
14251
14252     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14253                                         player->index_bit, dig_side);
14254
14255     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14256                                         player->index_bit, dig_side);
14257
14258     return MP_ACTION;
14259   }
14260   else
14261   {
14262     if (!PLAYER_SWITCHING(player, x, y))
14263     {
14264       player->is_switching = TRUE;
14265       player->switch_x = x;
14266       player->switch_y = y;
14267
14268       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14269                                  player->index_bit, dig_side);
14270       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14271                                           player->index_bit, dig_side);
14272
14273       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14274                                  player->index_bit, dig_side);
14275       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14276                                           player->index_bit, dig_side);
14277     }
14278
14279     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14280                                player->index_bit, dig_side);
14281     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14282                                         player->index_bit, dig_side);
14283
14284     return MP_NO_ACTION;
14285   }
14286
14287   player->push_delay = -1;
14288
14289   if (is_player)                // function can also be called by EL_PENGUIN
14290   {
14291     if (Feld[x][y] != element)          // really digged/collected something
14292     {
14293       player->is_collecting = !player->is_digging;
14294       player->is_active = TRUE;
14295     }
14296   }
14297
14298   return MP_MOVING;
14299 }
14300
14301 static boolean DigFieldByCE(int x, int y, int digging_element)
14302 {
14303   int element = Feld[x][y];
14304
14305   if (!IS_FREE(x, y))
14306   {
14307     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14308                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14309                   ACTION_BREAKING);
14310
14311     // no element can dig solid indestructible elements
14312     if (IS_INDESTRUCTIBLE(element) &&
14313         !IS_DIGGABLE(element) &&
14314         !IS_COLLECTIBLE(element))
14315       return FALSE;
14316
14317     if (AmoebaNr[x][y] &&
14318         (element == EL_AMOEBA_FULL ||
14319          element == EL_BD_AMOEBA ||
14320          element == EL_AMOEBA_GROWING))
14321     {
14322       AmoebaCnt[AmoebaNr[x][y]]--;
14323       AmoebaCnt2[AmoebaNr[x][y]]--;
14324     }
14325
14326     if (IS_MOVING(x, y))
14327       RemoveMovingField(x, y);
14328     else
14329     {
14330       RemoveField(x, y);
14331       TEST_DrawLevelField(x, y);
14332     }
14333
14334     // if digged element was about to explode, prevent the explosion
14335     ExplodeField[x][y] = EX_TYPE_NONE;
14336
14337     PlayLevelSoundAction(x, y, action);
14338   }
14339
14340   Store[x][y] = EL_EMPTY;
14341
14342   // this makes it possible to leave the removed element again
14343   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14344     Store[x][y] = element;
14345
14346   return TRUE;
14347 }
14348
14349 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14350 {
14351   int jx = player->jx, jy = player->jy;
14352   int x = jx + dx, y = jy + dy;
14353   int snap_direction = (dx == -1 ? MV_LEFT  :
14354                         dx == +1 ? MV_RIGHT :
14355                         dy == -1 ? MV_UP    :
14356                         dy == +1 ? MV_DOWN  : MV_NONE);
14357   boolean can_continue_snapping = (level.continuous_snapping &&
14358                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14359
14360   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14361     return FALSE;
14362
14363   if (!player->active || !IN_LEV_FIELD(x, y))
14364     return FALSE;
14365
14366   if (dx && dy)
14367     return FALSE;
14368
14369   if (!dx && !dy)
14370   {
14371     if (player->MovPos == 0)
14372       player->is_pushing = FALSE;
14373
14374     player->is_snapping = FALSE;
14375
14376     if (player->MovPos == 0)
14377     {
14378       player->is_moving = FALSE;
14379       player->is_digging = FALSE;
14380       player->is_collecting = FALSE;
14381     }
14382
14383     return FALSE;
14384   }
14385
14386   // prevent snapping with already pressed snap key when not allowed
14387   if (player->is_snapping && !can_continue_snapping)
14388     return FALSE;
14389
14390   player->MovDir = snap_direction;
14391
14392   if (player->MovPos == 0)
14393   {
14394     player->is_moving = FALSE;
14395     player->is_digging = FALSE;
14396     player->is_collecting = FALSE;
14397   }
14398
14399   player->is_dropping = FALSE;
14400   player->is_dropping_pressed = FALSE;
14401   player->drop_pressed_delay = 0;
14402
14403   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14404     return FALSE;
14405
14406   player->is_snapping = TRUE;
14407   player->is_active = TRUE;
14408
14409   if (player->MovPos == 0)
14410   {
14411     player->is_moving = FALSE;
14412     player->is_digging = FALSE;
14413     player->is_collecting = FALSE;
14414   }
14415
14416   if (player->MovPos != 0)      // prevent graphic bugs in versions < 2.2.0
14417     TEST_DrawLevelField(player->last_jx, player->last_jy);
14418
14419   TEST_DrawLevelField(x, y);
14420
14421   return TRUE;
14422 }
14423
14424 static boolean DropElement(struct PlayerInfo *player)
14425 {
14426   int old_element, new_element;
14427   int dropx = player->jx, dropy = player->jy;
14428   int drop_direction = player->MovDir;
14429   int drop_side = drop_direction;
14430   int drop_element = get_next_dropped_element(player);
14431
14432   /* do not drop an element on top of another element; when holding drop key
14433      pressed without moving, dropped element must move away before the next
14434      element can be dropped (this is especially important if the next element
14435      is dynamite, which can be placed on background for historical reasons) */
14436   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14437     return MP_ACTION;
14438
14439   if (IS_THROWABLE(drop_element))
14440   {
14441     dropx += GET_DX_FROM_DIR(drop_direction);
14442     dropy += GET_DY_FROM_DIR(drop_direction);
14443
14444     if (!IN_LEV_FIELD(dropx, dropy))
14445       return FALSE;
14446   }
14447
14448   old_element = Feld[dropx][dropy];     // old element at dropping position
14449   new_element = drop_element;           // default: no change when dropping
14450
14451   // check if player is active, not moving and ready to drop
14452   if (!player->active || player->MovPos || player->drop_delay > 0)
14453     return FALSE;
14454
14455   // check if player has anything that can be dropped
14456   if (new_element == EL_UNDEFINED)
14457     return FALSE;
14458
14459   // only set if player has anything that can be dropped
14460   player->is_dropping_pressed = TRUE;
14461
14462   // check if drop key was pressed long enough for EM style dynamite
14463   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14464     return FALSE;
14465
14466   // check if anything can be dropped at the current position
14467   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14468     return FALSE;
14469
14470   // collected custom elements can only be dropped on empty fields
14471   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14472     return FALSE;
14473
14474   if (old_element != EL_EMPTY)
14475     Back[dropx][dropy] = old_element;   // store old element on this field
14476
14477   ResetGfxAnimation(dropx, dropy);
14478   ResetRandomAnimationValue(dropx, dropy);
14479
14480   if (player->inventory_size > 0 ||
14481       player->inventory_infinite_element != EL_UNDEFINED)
14482   {
14483     if (player->inventory_size > 0)
14484     {
14485       player->inventory_size--;
14486
14487       DrawGameDoorValues();
14488
14489       if (new_element == EL_DYNAMITE)
14490         new_element = EL_DYNAMITE_ACTIVE;
14491       else if (new_element == EL_EM_DYNAMITE)
14492         new_element = EL_EM_DYNAMITE_ACTIVE;
14493       else if (new_element == EL_SP_DISK_RED)
14494         new_element = EL_SP_DISK_RED_ACTIVE;
14495     }
14496
14497     Feld[dropx][dropy] = new_element;
14498
14499     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14500       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14501                           el2img(Feld[dropx][dropy]), 0);
14502
14503     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14504
14505     // needed if previous element just changed to "empty" in the last frame
14506     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14507
14508     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14509                                player->index_bit, drop_side);
14510     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14511                                         CE_PLAYER_DROPS_X,
14512                                         player->index_bit, drop_side);
14513
14514     TestIfElementTouchesCustomElement(dropx, dropy);
14515   }
14516   else          // player is dropping a dyna bomb
14517   {
14518     player->dynabombs_left--;
14519
14520     Feld[dropx][dropy] = new_element;
14521
14522     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14523       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14524                           el2img(Feld[dropx][dropy]), 0);
14525
14526     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14527   }
14528
14529   if (Feld[dropx][dropy] == new_element) // uninitialized unless CE change
14530     InitField_WithBug1(dropx, dropy, FALSE);
14531
14532   new_element = Feld[dropx][dropy];     // element might have changed
14533
14534   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14535       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14536   {
14537     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14538       MovDir[dropx][dropy] = drop_direction;
14539
14540     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14541
14542     // do not cause impact style collision by dropping elements that can fall
14543     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14544   }
14545
14546   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14547   player->is_dropping = TRUE;
14548
14549   player->drop_pressed_delay = 0;
14550   player->is_dropping_pressed = FALSE;
14551
14552   player->drop_x = dropx;
14553   player->drop_y = dropy;
14554
14555   return TRUE;
14556 }
14557
14558 // ----------------------------------------------------------------------------
14559 // game sound playing functions
14560 // ----------------------------------------------------------------------------
14561
14562 static int *loop_sound_frame = NULL;
14563 static int *loop_sound_volume = NULL;
14564
14565 void InitPlayLevelSound(void)
14566 {
14567   int num_sounds = getSoundListSize();
14568
14569   checked_free(loop_sound_frame);
14570   checked_free(loop_sound_volume);
14571
14572   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14573   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14574 }
14575
14576 static void PlayLevelSound(int x, int y, int nr)
14577 {
14578   int sx = SCREENX(x), sy = SCREENY(y);
14579   int volume, stereo_position;
14580   int max_distance = 8;
14581   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14582
14583   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14584       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14585     return;
14586
14587   if (!IN_LEV_FIELD(x, y) ||
14588       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14589       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14590     return;
14591
14592   volume = SOUND_MAX_VOLUME;
14593
14594   if (!IN_SCR_FIELD(sx, sy))
14595   {
14596     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14597     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14598
14599     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14600   }
14601
14602   stereo_position = (SOUND_MAX_LEFT +
14603                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14604                      (SCR_FIELDX + 2 * max_distance));
14605
14606   if (IS_LOOP_SOUND(nr))
14607   {
14608     /* This assures that quieter loop sounds do not overwrite louder ones,
14609        while restarting sound volume comparison with each new game frame. */
14610
14611     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14612       return;
14613
14614     loop_sound_volume[nr] = volume;
14615     loop_sound_frame[nr] = FrameCounter;
14616   }
14617
14618   PlaySoundExt(nr, volume, stereo_position, type);
14619 }
14620
14621 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14622 {
14623   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14624                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14625                  y < LEVELY(BY1) ? LEVELY(BY1) :
14626                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14627                  sound_action);
14628 }
14629
14630 static void PlayLevelSoundAction(int x, int y, int action)
14631 {
14632   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14633 }
14634
14635 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14636 {
14637   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14638
14639   if (sound_effect != SND_UNDEFINED)
14640     PlayLevelSound(x, y, sound_effect);
14641 }
14642
14643 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14644                                               int action)
14645 {
14646   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14647
14648   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14649     PlayLevelSound(x, y, sound_effect);
14650 }
14651
14652 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14653 {
14654   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14655
14656   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14657     PlayLevelSound(x, y, sound_effect);
14658 }
14659
14660 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14661 {
14662   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14663
14664   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14665     StopSound(sound_effect);
14666 }
14667
14668 static int getLevelMusicNr(void)
14669 {
14670   if (levelset.music[level_nr] != MUS_UNDEFINED)
14671     return levelset.music[level_nr];            // from config file
14672   else
14673     return MAP_NOCONF_MUSIC(level_nr);          // from music dir
14674 }
14675
14676 static void FadeLevelSounds(void)
14677 {
14678   FadeSounds();
14679 }
14680
14681 static void FadeLevelMusic(void)
14682 {
14683   int music_nr = getLevelMusicNr();
14684   char *curr_music = getCurrentlyPlayingMusicFilename();
14685   char *next_music = getMusicInfoEntryFilename(music_nr);
14686
14687   if (!strEqual(curr_music, next_music))
14688     FadeMusic();
14689 }
14690
14691 void FadeLevelSoundsAndMusic(void)
14692 {
14693   FadeLevelSounds();
14694   FadeLevelMusic();
14695 }
14696
14697 static void PlayLevelMusic(void)
14698 {
14699   int music_nr = getLevelMusicNr();
14700   char *curr_music = getCurrentlyPlayingMusicFilename();
14701   char *next_music = getMusicInfoEntryFilename(music_nr);
14702
14703   if (!strEqual(curr_music, next_music))
14704     PlayMusicLoop(music_nr);
14705 }
14706
14707 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14708 {
14709   int element = (element_em > -1 ? map_element_EM_to_RND_game(element_em) : 0);
14710   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14711   int x = xx - 1 - offset;
14712   int y = yy - 1 - offset;
14713
14714   switch (sample)
14715   {
14716     case SOUND_blank:
14717       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14718       break;
14719
14720     case SOUND_roll:
14721       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14722       break;
14723
14724     case SOUND_stone:
14725       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14726       break;
14727
14728     case SOUND_nut:
14729       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14730       break;
14731
14732     case SOUND_crack:
14733       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14734       break;
14735
14736     case SOUND_bug:
14737       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14738       break;
14739
14740     case SOUND_tank:
14741       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14742       break;
14743
14744     case SOUND_android_clone:
14745       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14746       break;
14747
14748     case SOUND_android_move:
14749       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14750       break;
14751
14752     case SOUND_spring:
14753       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14754       break;
14755
14756     case SOUND_slurp:
14757       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14758       break;
14759
14760     case SOUND_eater:
14761       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14762       break;
14763
14764     case SOUND_eater_eat:
14765       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14766       break;
14767
14768     case SOUND_alien:
14769       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14770       break;
14771
14772     case SOUND_collect:
14773       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14774       break;
14775
14776     case SOUND_diamond:
14777       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14778       break;
14779
14780     case SOUND_squash:
14781       // !!! CHECK THIS !!!
14782 #if 1
14783       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14784 #else
14785       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14786 #endif
14787       break;
14788
14789     case SOUND_wonderfall:
14790       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14791       break;
14792
14793     case SOUND_drip:
14794       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14795       break;
14796
14797     case SOUND_push:
14798       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14799       break;
14800
14801     case SOUND_dirt:
14802       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14803       break;
14804
14805     case SOUND_acid:
14806       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14807       break;
14808
14809     case SOUND_ball:
14810       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14811       break;
14812
14813     case SOUND_slide:
14814       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14815       break;
14816
14817     case SOUND_wonder:
14818       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14819       break;
14820
14821     case SOUND_door:
14822       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14823       break;
14824
14825     case SOUND_exit_open:
14826       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14827       break;
14828
14829     case SOUND_exit_leave:
14830       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14831       break;
14832
14833     case SOUND_dynamite:
14834       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14835       break;
14836
14837     case SOUND_tick:
14838       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14839       break;
14840
14841     case SOUND_press:
14842       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14843       break;
14844
14845     case SOUND_wheel:
14846       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14847       break;
14848
14849     case SOUND_boom:
14850       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14851       break;
14852
14853     case SOUND_die:
14854       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14855       break;
14856
14857     case SOUND_time:
14858       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14859       break;
14860
14861     default:
14862       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14863       break;
14864   }
14865 }
14866
14867 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
14868 {
14869   int element = map_element_SP_to_RND(element_sp);
14870   int action = map_action_SP_to_RND(action_sp);
14871   int offset = (setup.sp_show_border_elements ? 0 : 1);
14872   int x = xx - offset;
14873   int y = yy - offset;
14874
14875   PlayLevelSoundElementAction(x, y, element, action);
14876 }
14877
14878 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
14879 {
14880   int element = map_element_MM_to_RND(element_mm);
14881   int action = map_action_MM_to_RND(action_mm);
14882   int offset = 0;
14883   int x = xx - offset;
14884   int y = yy - offset;
14885
14886   if (!IS_MM_ELEMENT(element))
14887     element = EL_MM_DEFAULT;
14888
14889   PlayLevelSoundElementAction(x, y, element, action);
14890 }
14891
14892 void PlaySound_MM(int sound_mm)
14893 {
14894   int sound = map_sound_MM_to_RND(sound_mm);
14895
14896   if (sound == SND_UNDEFINED)
14897     return;
14898
14899   PlaySound(sound);
14900 }
14901
14902 void PlaySoundLoop_MM(int sound_mm)
14903 {
14904   int sound = map_sound_MM_to_RND(sound_mm);
14905
14906   if (sound == SND_UNDEFINED)
14907     return;
14908
14909   PlaySoundLoop(sound);
14910 }
14911
14912 void StopSound_MM(int sound_mm)
14913 {
14914   int sound = map_sound_MM_to_RND(sound_mm);
14915
14916   if (sound == SND_UNDEFINED)
14917     return;
14918
14919   StopSound(sound);
14920 }
14921
14922 void RaiseScore(int value)
14923 {
14924   game.score += value;
14925
14926   game_panel_controls[GAME_PANEL_SCORE].value = game.score;
14927
14928   DisplayGameControlValues();
14929 }
14930
14931 void RaiseScoreElement(int element)
14932 {
14933   switch (element)
14934   {
14935     case EL_EMERALD:
14936     case EL_BD_DIAMOND:
14937     case EL_EMERALD_YELLOW:
14938     case EL_EMERALD_RED:
14939     case EL_EMERALD_PURPLE:
14940     case EL_SP_INFOTRON:
14941       RaiseScore(level.score[SC_EMERALD]);
14942       break;
14943     case EL_DIAMOND:
14944       RaiseScore(level.score[SC_DIAMOND]);
14945       break;
14946     case EL_CRYSTAL:
14947       RaiseScore(level.score[SC_CRYSTAL]);
14948       break;
14949     case EL_PEARL:
14950       RaiseScore(level.score[SC_PEARL]);
14951       break;
14952     case EL_BUG:
14953     case EL_BD_BUTTERFLY:
14954     case EL_SP_ELECTRON:
14955       RaiseScore(level.score[SC_BUG]);
14956       break;
14957     case EL_SPACESHIP:
14958     case EL_BD_FIREFLY:
14959     case EL_SP_SNIKSNAK:
14960       RaiseScore(level.score[SC_SPACESHIP]);
14961       break;
14962     case EL_YAMYAM:
14963     case EL_DARK_YAMYAM:
14964       RaiseScore(level.score[SC_YAMYAM]);
14965       break;
14966     case EL_ROBOT:
14967       RaiseScore(level.score[SC_ROBOT]);
14968       break;
14969     case EL_PACMAN:
14970       RaiseScore(level.score[SC_PACMAN]);
14971       break;
14972     case EL_NUT:
14973       RaiseScore(level.score[SC_NUT]);
14974       break;
14975     case EL_DYNAMITE:
14976     case EL_EM_DYNAMITE:
14977     case EL_SP_DISK_RED:
14978     case EL_DYNABOMB_INCREASE_NUMBER:
14979     case EL_DYNABOMB_INCREASE_SIZE:
14980     case EL_DYNABOMB_INCREASE_POWER:
14981       RaiseScore(level.score[SC_DYNAMITE]);
14982       break;
14983     case EL_SHIELD_NORMAL:
14984     case EL_SHIELD_DEADLY:
14985       RaiseScore(level.score[SC_SHIELD]);
14986       break;
14987     case EL_EXTRA_TIME:
14988       RaiseScore(level.extra_time_score);
14989       break;
14990     case EL_KEY_1:
14991     case EL_KEY_2:
14992     case EL_KEY_3:
14993     case EL_KEY_4:
14994     case EL_EM_KEY_1:
14995     case EL_EM_KEY_2:
14996     case EL_EM_KEY_3:
14997     case EL_EM_KEY_4:
14998     case EL_EMC_KEY_5:
14999     case EL_EMC_KEY_6:
15000     case EL_EMC_KEY_7:
15001     case EL_EMC_KEY_8:
15002     case EL_DC_KEY_WHITE:
15003       RaiseScore(level.score[SC_KEY]);
15004       break;
15005     default:
15006       RaiseScore(element_info[element].collect_score);
15007       break;
15008   }
15009 }
15010
15011 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15012 {
15013   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15014   {
15015     // closing door required in case of envelope style request dialogs
15016     if (!skip_request)
15017     {
15018       // prevent short reactivation of overlay buttons while closing door
15019       SetOverlayActive(FALSE);
15020
15021       CloseDoor(DOOR_CLOSE_1);
15022     }
15023
15024     if (network.enabled)
15025       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15026     else
15027     {
15028       if (quick_quit)
15029         FadeSkipNextFadeIn();
15030
15031       SetGameStatus(GAME_MODE_MAIN);
15032
15033       DrawMainMenu();
15034     }
15035   }
15036   else          // continue playing the game
15037   {
15038     if (tape.playing && tape.deactivate_display)
15039       TapeDeactivateDisplayOff(TRUE);
15040
15041     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15042
15043     if (tape.playing && tape.deactivate_display)
15044       TapeDeactivateDisplayOn();
15045   }
15046 }
15047
15048 void RequestQuitGame(boolean ask_if_really_quit)
15049 {
15050   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
15051   boolean skip_request = game.all_players_gone || quick_quit;
15052
15053   RequestQuitGameExt(skip_request, quick_quit,
15054                      "Do you really want to quit the game?");
15055 }
15056
15057 void RequestRestartGame(char *message)
15058 {
15059   game.restart_game_message = NULL;
15060
15061   boolean has_started_game = hasStartedNetworkGame();
15062   int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM);
15063
15064   if (Request(message, request_mode | REQ_STAY_CLOSED) && has_started_game)
15065   {
15066     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15067   }
15068   else
15069   {
15070     SetGameStatus(GAME_MODE_MAIN);
15071
15072     DrawMainMenu();
15073   }
15074 }
15075
15076 void CheckGameOver(void)
15077 {
15078   static boolean last_game_over = FALSE;
15079   static int game_over_delay = 0;
15080   int game_over_delay_value = 50;
15081   boolean game_over = checkGameFailed();
15082
15083   // do not handle game over if request dialog is already active
15084   if (game.request_active)
15085     return;
15086
15087   // do not ask to play again if game was never actually played
15088   if (!game.GamePlayed)
15089     return;
15090
15091   if (!game_over)
15092   {
15093     last_game_over = FALSE;
15094     game_over_delay = game_over_delay_value;
15095
15096     return;
15097   }
15098
15099   if (game_over_delay > 0)
15100   {
15101     game_over_delay--;
15102
15103     return;
15104   }
15105
15106   if (last_game_over != game_over)
15107     game.restart_game_message = (hasStartedNetworkGame() ?
15108                                  "Game over! Play it again?" :
15109                                  "Game over!");
15110
15111   last_game_over = game_over;
15112 }
15113
15114 boolean checkGameSolved(void)
15115 {
15116   // set for all game engines if level was solved
15117   return game.LevelSolved_GameEnd;
15118 }
15119
15120 boolean checkGameFailed(void)
15121 {
15122   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15123     return (game_em.game_over && !game_em.level_solved);
15124   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15125     return (game_sp.game_over && !game_sp.level_solved);
15126   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15127     return (game_mm.game_over && !game_mm.level_solved);
15128   else                          // GAME_ENGINE_TYPE_RND
15129     return (game.GameOver && !game.LevelSolved);
15130 }
15131
15132 boolean checkGameEnded(void)
15133 {
15134   return (checkGameSolved() || checkGameFailed());
15135 }
15136
15137
15138 // ----------------------------------------------------------------------------
15139 // random generator functions
15140 // ----------------------------------------------------------------------------
15141
15142 unsigned int InitEngineRandom_RND(int seed)
15143 {
15144   game.num_random_calls = 0;
15145
15146   return InitEngineRandom(seed);
15147 }
15148
15149 unsigned int RND(int max)
15150 {
15151   if (max > 0)
15152   {
15153     game.num_random_calls++;
15154
15155     return GetEngineRandom(max);
15156   }
15157
15158   return 0;
15159 }
15160
15161
15162 // ----------------------------------------------------------------------------
15163 // game engine snapshot handling functions
15164 // ----------------------------------------------------------------------------
15165
15166 struct EngineSnapshotInfo
15167 {
15168   // runtime values for custom element collect score
15169   int collect_score[NUM_CUSTOM_ELEMENTS];
15170
15171   // runtime values for group element choice position
15172   int choice_pos[NUM_GROUP_ELEMENTS];
15173
15174   // runtime values for belt position animations
15175   int belt_graphic[4][NUM_BELT_PARTS];
15176   int belt_anim_mode[4][NUM_BELT_PARTS];
15177 };
15178
15179 static struct EngineSnapshotInfo engine_snapshot_rnd;
15180 static char *snapshot_level_identifier = NULL;
15181 static int snapshot_level_nr = -1;
15182
15183 static void SaveEngineSnapshotValues_RND(void)
15184 {
15185   static int belt_base_active_element[4] =
15186   {
15187     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15188     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15189     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15190     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15191   };
15192   int i, j;
15193
15194   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15195   {
15196     int element = EL_CUSTOM_START + i;
15197
15198     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15199   }
15200
15201   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15202   {
15203     int element = EL_GROUP_START + i;
15204
15205     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15206   }
15207
15208   for (i = 0; i < 4; i++)
15209   {
15210     for (j = 0; j < NUM_BELT_PARTS; j++)
15211     {
15212       int element = belt_base_active_element[i] + j;
15213       int graphic = el2img(element);
15214       int anim_mode = graphic_info[graphic].anim_mode;
15215
15216       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15217       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15218     }
15219   }
15220 }
15221
15222 static void LoadEngineSnapshotValues_RND(void)
15223 {
15224   unsigned int num_random_calls = game.num_random_calls;
15225   int i, j;
15226
15227   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15228   {
15229     int element = EL_CUSTOM_START + i;
15230
15231     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15232   }
15233
15234   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15235   {
15236     int element = EL_GROUP_START + i;
15237
15238     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15239   }
15240
15241   for (i = 0; i < 4; i++)
15242   {
15243     for (j = 0; j < NUM_BELT_PARTS; j++)
15244     {
15245       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15246       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15247
15248       graphic_info[graphic].anim_mode = anim_mode;
15249     }
15250   }
15251
15252   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15253   {
15254     InitRND(tape.random_seed);
15255     for (i = 0; i < num_random_calls; i++)
15256       RND(1);
15257   }
15258
15259   if (game.num_random_calls != num_random_calls)
15260   {
15261     Error(ERR_INFO, "number of random calls out of sync");
15262     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
15263     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
15264     Error(ERR_EXIT, "this should not happen -- please debug");
15265   }
15266 }
15267
15268 void FreeEngineSnapshotSingle(void)
15269 {
15270   FreeSnapshotSingle();
15271
15272   setString(&snapshot_level_identifier, NULL);
15273   snapshot_level_nr = -1;
15274 }
15275
15276 void FreeEngineSnapshotList(void)
15277 {
15278   FreeSnapshotList();
15279 }
15280
15281 static ListNode *SaveEngineSnapshotBuffers(void)
15282 {
15283   ListNode *buffers = NULL;
15284
15285   // copy some special values to a structure better suited for the snapshot
15286
15287   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15288     SaveEngineSnapshotValues_RND();
15289   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15290     SaveEngineSnapshotValues_EM();
15291   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15292     SaveEngineSnapshotValues_SP(&buffers);
15293   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15294     SaveEngineSnapshotValues_MM(&buffers);
15295
15296   // save values stored in special snapshot structure
15297
15298   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15299     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15300   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15301     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15302   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15303     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15304   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15305     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15306
15307   // save further RND engine values
15308
15309   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15310   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15311   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15312
15313   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15314   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15315   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15316   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15317   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15318
15319   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15320   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15321   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15322
15323   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15324
15325   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15326   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15327
15328   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Feld));
15329   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15330   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15331   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15332   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15333   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15334   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15335   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15336   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15337   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15338   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15339   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15340   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15341   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15342   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15343   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15344   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15345   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15346
15347   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15348   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15349
15350   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15351   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15352   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15353
15354   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15355   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15356
15357   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15358   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15359   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15360   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15361   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15362
15363   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15364   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15365
15366 #if 0
15367   ListNode *node = engine_snapshot_list_rnd;
15368   int num_bytes = 0;
15369
15370   while (node != NULL)
15371   {
15372     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15373
15374     node = node->next;
15375   }
15376
15377   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
15378 #endif
15379
15380   return buffers;
15381 }
15382
15383 void SaveEngineSnapshotSingle(void)
15384 {
15385   ListNode *buffers = SaveEngineSnapshotBuffers();
15386
15387   // finally save all snapshot buffers to single snapshot
15388   SaveSnapshotSingle(buffers);
15389
15390   // save level identification information
15391   setString(&snapshot_level_identifier, leveldir_current->identifier);
15392   snapshot_level_nr = level_nr;
15393 }
15394
15395 boolean CheckSaveEngineSnapshotToList(void)
15396 {
15397   boolean save_snapshot =
15398     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
15399      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
15400       game.snapshot.changed_action) ||
15401      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15402       game.snapshot.collected_item));
15403
15404   game.snapshot.changed_action = FALSE;
15405   game.snapshot.collected_item = FALSE;
15406   game.snapshot.save_snapshot = save_snapshot;
15407
15408   return save_snapshot;
15409 }
15410
15411 void SaveEngineSnapshotToList(void)
15412 {
15413   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
15414       tape.quick_resume)
15415     return;
15416
15417   ListNode *buffers = SaveEngineSnapshotBuffers();
15418
15419   // finally save all snapshot buffers to snapshot list
15420   SaveSnapshotToList(buffers);
15421 }
15422
15423 void SaveEngineSnapshotToListInitial(void)
15424 {
15425   FreeEngineSnapshotList();
15426
15427   SaveEngineSnapshotToList();
15428 }
15429
15430 static void LoadEngineSnapshotValues(void)
15431 {
15432   // restore special values from snapshot structure
15433
15434   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15435     LoadEngineSnapshotValues_RND();
15436   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15437     LoadEngineSnapshotValues_EM();
15438   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15439     LoadEngineSnapshotValues_SP();
15440   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15441     LoadEngineSnapshotValues_MM();
15442 }
15443
15444 void LoadEngineSnapshotSingle(void)
15445 {
15446   LoadSnapshotSingle();
15447
15448   LoadEngineSnapshotValues();
15449 }
15450
15451 static void LoadEngineSnapshot_Undo(int steps)
15452 {
15453   LoadSnapshotFromList_Older(steps);
15454
15455   LoadEngineSnapshotValues();
15456 }
15457
15458 static void LoadEngineSnapshot_Redo(int steps)
15459 {
15460   LoadSnapshotFromList_Newer(steps);
15461
15462   LoadEngineSnapshotValues();
15463 }
15464
15465 boolean CheckEngineSnapshotSingle(void)
15466 {
15467   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15468           snapshot_level_nr == level_nr);
15469 }
15470
15471 boolean CheckEngineSnapshotList(void)
15472 {
15473   return CheckSnapshotList();
15474 }
15475
15476
15477 // ---------- new game button stuff -------------------------------------------
15478
15479 static struct
15480 {
15481   int graphic;
15482   struct XY *pos;
15483   int gadget_id;
15484   boolean *setup_value;
15485   boolean allowed_on_tape;
15486   boolean is_touch_button;
15487   char *infotext;
15488 } gamebutton_info[NUM_GAME_BUTTONS] =
15489 {
15490   {
15491     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
15492     GAME_CTRL_ID_STOP,                          NULL,
15493     TRUE, FALSE,                                "stop game"
15494   },
15495   {
15496     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
15497     GAME_CTRL_ID_PAUSE,                         NULL,
15498     TRUE, FALSE,                                "pause game"
15499   },
15500   {
15501     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
15502     GAME_CTRL_ID_PLAY,                          NULL,
15503     TRUE, FALSE,                                "play game"
15504   },
15505   {
15506     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
15507     GAME_CTRL_ID_UNDO,                          NULL,
15508     TRUE, FALSE,                                "undo step"
15509   },
15510   {
15511     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
15512     GAME_CTRL_ID_REDO,                          NULL,
15513     TRUE, FALSE,                                "redo step"
15514   },
15515   {
15516     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
15517     GAME_CTRL_ID_SAVE,                          NULL,
15518     TRUE, FALSE,                                "save game"
15519   },
15520   {
15521     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
15522     GAME_CTRL_ID_PAUSE2,                        NULL,
15523     TRUE, FALSE,                                "pause game"
15524   },
15525   {
15526     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
15527     GAME_CTRL_ID_LOAD,                          NULL,
15528     TRUE, FALSE,                                "load game"
15529   },
15530   {
15531     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
15532     GAME_CTRL_ID_PANEL_STOP,                    NULL,
15533     FALSE, FALSE,                               "stop game"
15534   },
15535   {
15536     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
15537     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
15538     FALSE, FALSE,                               "pause game"
15539   },
15540   {
15541     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
15542     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
15543     FALSE, FALSE,                               "play game"
15544   },
15545   {
15546     IMG_GFX_GAME_BUTTON_TOUCH_STOP,             &game.button.touch_stop,
15547     GAME_CTRL_ID_TOUCH_STOP,                    NULL,
15548     FALSE, TRUE,                                "stop game"
15549   },
15550   {
15551     IMG_GFX_GAME_BUTTON_TOUCH_PAUSE,            &game.button.touch_pause,
15552     GAME_CTRL_ID_TOUCH_PAUSE,                   NULL,
15553     FALSE, TRUE,                                "pause game"
15554   },
15555   {
15556     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
15557     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
15558     TRUE, FALSE,                                "background music on/off"
15559   },
15560   {
15561     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
15562     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
15563     TRUE, FALSE,                                "sound loops on/off"
15564   },
15565   {
15566     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
15567     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
15568     TRUE, FALSE,                                "normal sounds on/off"
15569   },
15570   {
15571     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
15572     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
15573     FALSE, FALSE,                               "background music on/off"
15574   },
15575   {
15576     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
15577     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
15578     FALSE, FALSE,                               "sound loops on/off"
15579   },
15580   {
15581     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
15582     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
15583     FALSE, FALSE,                               "normal sounds on/off"
15584   }
15585 };
15586
15587 void CreateGameButtons(void)
15588 {
15589   int i;
15590
15591   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15592   {
15593     int graphic = gamebutton_info[i].graphic;
15594     struct GraphicInfo *gfx = &graphic_info[graphic];
15595     struct XY *pos = gamebutton_info[i].pos;
15596     struct GadgetInfo *gi;
15597     int button_type;
15598     boolean checked;
15599     unsigned int event_mask;
15600     boolean is_touch_button = gamebutton_info[i].is_touch_button;
15601     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
15602     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
15603     int base_x = (is_touch_button ? 0 : on_tape ? VX : DX);
15604     int base_y = (is_touch_button ? 0 : on_tape ? VY : DY);
15605     int gd_x   = gfx->src_x;
15606     int gd_y   = gfx->src_y;
15607     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
15608     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
15609     int gd_xa  = gfx->src_x + gfx->active_xoffset;
15610     int gd_ya  = gfx->src_y + gfx->active_yoffset;
15611     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
15612     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
15613     int x = (is_touch_button ? pos->x : GDI_ACTIVE_POS(pos->x));
15614     int y = (is_touch_button ? pos->y : GDI_ACTIVE_POS(pos->y));
15615     int id = i;
15616
15617     if (gfx->bitmap == NULL)
15618     {
15619       game_gadget[id] = NULL;
15620
15621       continue;
15622     }
15623
15624     if (id == GAME_CTRL_ID_STOP ||
15625         id == GAME_CTRL_ID_PANEL_STOP ||
15626         id == GAME_CTRL_ID_TOUCH_STOP ||
15627         id == GAME_CTRL_ID_PLAY ||
15628         id == GAME_CTRL_ID_PANEL_PLAY ||
15629         id == GAME_CTRL_ID_SAVE ||
15630         id == GAME_CTRL_ID_LOAD)
15631     {
15632       button_type = GD_TYPE_NORMAL_BUTTON;
15633       checked = FALSE;
15634       event_mask = GD_EVENT_RELEASED;
15635     }
15636     else if (id == GAME_CTRL_ID_UNDO ||
15637              id == GAME_CTRL_ID_REDO)
15638     {
15639       button_type = GD_TYPE_NORMAL_BUTTON;
15640       checked = FALSE;
15641       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
15642     }
15643     else
15644     {
15645       button_type = GD_TYPE_CHECK_BUTTON;
15646       checked = (gamebutton_info[i].setup_value != NULL ?
15647                  *gamebutton_info[i].setup_value : FALSE);
15648       event_mask = GD_EVENT_PRESSED;
15649     }
15650
15651     gi = CreateGadget(GDI_CUSTOM_ID, id,
15652                       GDI_IMAGE_ID, graphic,
15653                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
15654                       GDI_X, base_x + x,
15655                       GDI_Y, base_y + y,
15656                       GDI_WIDTH, gfx->width,
15657                       GDI_HEIGHT, gfx->height,
15658                       GDI_TYPE, button_type,
15659                       GDI_STATE, GD_BUTTON_UNPRESSED,
15660                       GDI_CHECKED, checked,
15661                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
15662                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
15663                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
15664                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
15665                       GDI_DIRECT_DRAW, FALSE,
15666                       GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
15667                       GDI_EVENT_MASK, event_mask,
15668                       GDI_CALLBACK_ACTION, HandleGameButtons,
15669                       GDI_END);
15670
15671     if (gi == NULL)
15672       Error(ERR_EXIT, "cannot create gadget");
15673
15674     game_gadget[id] = gi;
15675   }
15676 }
15677
15678 void FreeGameButtons(void)
15679 {
15680   int i;
15681
15682   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15683     FreeGadget(game_gadget[i]);
15684 }
15685
15686 static void UnmapGameButtonsAtSamePosition(int id)
15687 {
15688   int i;
15689
15690   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15691     if (i != id &&
15692         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15693         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15694       UnmapGadget(game_gadget[i]);
15695 }
15696
15697 static void UnmapGameButtonsAtSamePosition_All(void)
15698 {
15699   if (setup.show_snapshot_buttons)
15700   {
15701     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
15702     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
15703     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
15704   }
15705   else
15706   {
15707     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
15708     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
15709     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
15710
15711     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
15712     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
15713     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
15714   }
15715 }
15716
15717 static void MapGameButtonsAtSamePosition(int id)
15718 {
15719   int i;
15720
15721   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15722     if (i != id &&
15723         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15724         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15725       MapGadget(game_gadget[i]);
15726
15727   UnmapGameButtonsAtSamePosition_All();
15728 }
15729
15730 void MapUndoRedoButtons(void)
15731 {
15732   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15733   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15734
15735   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15736   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15737 }
15738
15739 void UnmapUndoRedoButtons(void)
15740 {
15741   UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15742   UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15743
15744   MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15745   MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15746 }
15747
15748 void ModifyPauseButtons(void)
15749 {
15750   static int ids[] =
15751   {
15752     GAME_CTRL_ID_PAUSE,
15753     GAME_CTRL_ID_PAUSE2,
15754     GAME_CTRL_ID_PANEL_PAUSE,
15755     GAME_CTRL_ID_TOUCH_PAUSE,
15756     -1
15757   };
15758   int i;
15759
15760   for (i = 0; ids[i] > -1; i++)
15761     ModifyGadget(game_gadget[ids[i]], GDI_CHECKED, tape.pausing, GDI_END);
15762 }
15763
15764 static void MapGameButtonsExt(boolean on_tape)
15765 {
15766   int i;
15767
15768   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15769     if ((!on_tape || gamebutton_info[i].allowed_on_tape) &&
15770         i != GAME_CTRL_ID_UNDO &&
15771         i != GAME_CTRL_ID_REDO)
15772       MapGadget(game_gadget[i]);
15773
15774   UnmapGameButtonsAtSamePosition_All();
15775
15776   RedrawGameButtons();
15777 }
15778
15779 static void UnmapGameButtonsExt(boolean on_tape)
15780 {
15781   int i;
15782
15783   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15784     if (!on_tape || gamebutton_info[i].allowed_on_tape)
15785       UnmapGadget(game_gadget[i]);
15786 }
15787
15788 static void RedrawGameButtonsExt(boolean on_tape)
15789 {
15790   int i;
15791
15792   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15793     if (!on_tape || gamebutton_info[i].allowed_on_tape)
15794       RedrawGadget(game_gadget[i]);
15795 }
15796
15797 static void SetGadgetState(struct GadgetInfo *gi, boolean state)
15798 {
15799   if (gi == NULL)
15800     return;
15801
15802   gi->checked = state;
15803 }
15804
15805 static void RedrawSoundButtonGadget(int id)
15806 {
15807   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
15808              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
15809              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
15810              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
15811              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
15812              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
15813              id);
15814
15815   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
15816   RedrawGadget(game_gadget[id2]);
15817 }
15818
15819 void MapGameButtons(void)
15820 {
15821   MapGameButtonsExt(FALSE);
15822 }
15823
15824 void UnmapGameButtons(void)
15825 {
15826   UnmapGameButtonsExt(FALSE);
15827 }
15828
15829 void RedrawGameButtons(void)
15830 {
15831   RedrawGameButtonsExt(FALSE);
15832 }
15833
15834 void MapGameButtonsOnTape(void)
15835 {
15836   MapGameButtonsExt(TRUE);
15837 }
15838
15839 void UnmapGameButtonsOnTape(void)
15840 {
15841   UnmapGameButtonsExt(TRUE);
15842 }
15843
15844 void RedrawGameButtonsOnTape(void)
15845 {
15846   RedrawGameButtonsExt(TRUE);
15847 }
15848
15849 static void GameUndoRedoExt(void)
15850 {
15851   ClearPlayerAction();
15852
15853   tape.pausing = TRUE;
15854
15855   RedrawPlayfield();
15856   UpdateAndDisplayGameControlValues();
15857
15858   DrawCompleteVideoDisplay();
15859   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
15860   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
15861   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
15862
15863   BackToFront();
15864 }
15865
15866 static void GameUndo(int steps)
15867 {
15868   if (!CheckEngineSnapshotList())
15869     return;
15870
15871   LoadEngineSnapshot_Undo(steps);
15872
15873   GameUndoRedoExt();
15874 }
15875
15876 static void GameRedo(int steps)
15877 {
15878   if (!CheckEngineSnapshotList())
15879     return;
15880
15881   LoadEngineSnapshot_Redo(steps);
15882
15883   GameUndoRedoExt();
15884 }
15885
15886 static void HandleGameButtonsExt(int id, int button)
15887 {
15888   static boolean game_undo_executed = FALSE;
15889   int steps = BUTTON_STEPSIZE(button);
15890   boolean handle_game_buttons =
15891     (game_status == GAME_MODE_PLAYING ||
15892      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
15893
15894   if (!handle_game_buttons)
15895     return;
15896
15897   switch (id)
15898   {
15899     case GAME_CTRL_ID_STOP:
15900     case GAME_CTRL_ID_PANEL_STOP:
15901     case GAME_CTRL_ID_TOUCH_STOP:
15902       if (game_status == GAME_MODE_MAIN)
15903         break;
15904
15905       if (tape.playing)
15906         TapeStop();
15907       else
15908         RequestQuitGame(TRUE);
15909
15910       break;
15911
15912     case GAME_CTRL_ID_PAUSE:
15913     case GAME_CTRL_ID_PAUSE2:
15914     case GAME_CTRL_ID_PANEL_PAUSE:
15915     case GAME_CTRL_ID_TOUCH_PAUSE:
15916       if (network.enabled && game_status == GAME_MODE_PLAYING)
15917       {
15918         if (tape.pausing)
15919           SendToServer_ContinuePlaying();
15920         else
15921           SendToServer_PausePlaying();
15922       }
15923       else
15924         TapeTogglePause(TAPE_TOGGLE_MANUAL);
15925
15926       game_undo_executed = FALSE;
15927
15928       break;
15929
15930     case GAME_CTRL_ID_PLAY:
15931     case GAME_CTRL_ID_PANEL_PLAY:
15932       if (game_status == GAME_MODE_MAIN)
15933       {
15934         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15935       }
15936       else if (tape.pausing)
15937       {
15938         if (network.enabled)
15939           SendToServer_ContinuePlaying();
15940         else
15941           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
15942       }
15943       break;
15944
15945     case GAME_CTRL_ID_UNDO:
15946       // Important: When using "save snapshot when collecting an item" mode,
15947       // load last (current) snapshot for first "undo" after pressing "pause"
15948       // (else the last-but-one snapshot would be loaded, because the snapshot
15949       // pointer already points to the last snapshot when pressing "pause",
15950       // which is fine for "every step/move" mode, but not for "every collect")
15951       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15952           !game_undo_executed)
15953         steps--;
15954
15955       game_undo_executed = TRUE;
15956
15957       GameUndo(steps);
15958       break;
15959
15960     case GAME_CTRL_ID_REDO:
15961       GameRedo(steps);
15962       break;
15963
15964     case GAME_CTRL_ID_SAVE:
15965       TapeQuickSave();
15966       break;
15967
15968     case GAME_CTRL_ID_LOAD:
15969       TapeQuickLoad();
15970       break;
15971
15972     case SOUND_CTRL_ID_MUSIC:
15973     case SOUND_CTRL_ID_PANEL_MUSIC:
15974       if (setup.sound_music)
15975       { 
15976         setup.sound_music = FALSE;
15977
15978         FadeMusic();
15979       }
15980       else if (audio.music_available)
15981       { 
15982         setup.sound = setup.sound_music = TRUE;
15983
15984         SetAudioMode(setup.sound);
15985
15986         if (game_status == GAME_MODE_PLAYING)
15987           PlayLevelMusic();
15988       }
15989
15990       RedrawSoundButtonGadget(id);
15991
15992       break;
15993
15994     case SOUND_CTRL_ID_LOOPS:
15995     case SOUND_CTRL_ID_PANEL_LOOPS:
15996       if (setup.sound_loops)
15997         setup.sound_loops = FALSE;
15998       else if (audio.loops_available)
15999       {
16000         setup.sound = setup.sound_loops = TRUE;
16001
16002         SetAudioMode(setup.sound);
16003       }
16004
16005       RedrawSoundButtonGadget(id);
16006
16007       break;
16008
16009     case SOUND_CTRL_ID_SIMPLE:
16010     case SOUND_CTRL_ID_PANEL_SIMPLE:
16011       if (setup.sound_simple)
16012         setup.sound_simple = FALSE;
16013       else if (audio.sound_available)
16014       {
16015         setup.sound = setup.sound_simple = TRUE;
16016
16017         SetAudioMode(setup.sound);
16018       }
16019
16020       RedrawSoundButtonGadget(id);
16021
16022       break;
16023
16024     default:
16025       break;
16026   }
16027 }
16028
16029 static void HandleGameButtons(struct GadgetInfo *gi)
16030 {
16031   HandleGameButtonsExt(gi->custom_id, gi->event.button);
16032 }
16033
16034 void HandleSoundButtonKeys(Key key)
16035 {
16036   if (key == setup.shortcut.sound_simple)
16037     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
16038   else if (key == setup.shortcut.sound_loops)
16039     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
16040   else if (key == setup.shortcut.sound_music)
16041     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
16042 }