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