2fb1b686c7ae07a5482a68cc21e459993164d4b1
[rocksndiamonds.git] / src / game.c
1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // game.c
10 // ============================================================================
11
12 #include "libgame/libgame.h"
13
14 #include "game.h"
15 #include "init.h"
16 #include "tools.h"
17 #include "screens.h"
18 #include "events.h"
19 #include "files.h"
20 #include "tape.h"
21 #include "network.h"
22 #include "anim.h"
23
24
25 // DEBUG SETTINGS
26 #define DEBUG_INIT_PLAYER       1
27 #define DEBUG_PLAYER_ACTIONS    0
28
29 // EXPERIMENTAL STUFF
30 #define USE_NEW_AMOEBA_CODE     FALSE
31
32 // EXPERIMENTAL STUFF
33 #define USE_QUICKSAND_BD_ROCK_BUGFIX    0
34 #define USE_QUICKSAND_IMPACT_BUGFIX     0
35 #define USE_DELAYED_GFX_REDRAW          0
36 #define USE_NEW_PLAYER_ASSIGNMENTS      1
37
38 #if USE_DELAYED_GFX_REDRAW
39 #define TEST_DrawLevelField(x, y)                               \
40         GfxRedraw[x][y] |= GFX_REDRAW_TILE
41 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
42         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
43 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
44         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
45 #define TEST_DrawTwinkleOnField(x, y)                           \
46         GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
47 #else
48 #define TEST_DrawLevelField(x, y)                               \
49              DrawLevelField(x, y)
50 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
51              DrawLevelFieldCrumbled(x, y)
52 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
53              DrawLevelFieldCrumbledNeighbours(x, y)
54 #define TEST_DrawTwinkleOnField(x, y)                           \
55              DrawTwinkleOnField(x, y)
56 #endif
57
58
59 // for DigField()
60 #define DF_NO_PUSH              0
61 #define DF_DIG                  1
62 #define DF_SNAP                 2
63
64 // for MovePlayer()
65 #define MP_NO_ACTION            0
66 #define MP_MOVING               1
67 #define MP_ACTION               2
68 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
69
70 // for ScrollPlayer()
71 #define SCROLL_INIT             0
72 #define SCROLL_GO_ON            1
73
74 // for Bang()/Explode()
75 #define EX_PHASE_START          0
76 #define EX_TYPE_NONE            0
77 #define EX_TYPE_NORMAL          (1 << 0)
78 #define EX_TYPE_CENTER          (1 << 1)
79 #define EX_TYPE_BORDER          (1 << 2)
80 #define EX_TYPE_CROSS           (1 << 3)
81 #define EX_TYPE_DYNA            (1 << 4)
82 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
83
84 #define PANEL_OFF()             (game.panel.active == FALSE)
85 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
86 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
87 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
88
89 // game panel display and control definitions
90 #define GAME_PANEL_LEVEL_NUMBER                 0
91 #define GAME_PANEL_GEMS                         1
92 #define GAME_PANEL_INVENTORY_COUNT              2
93 #define GAME_PANEL_INVENTORY_FIRST_1            3
94 #define GAME_PANEL_INVENTORY_FIRST_2            4
95 #define GAME_PANEL_INVENTORY_FIRST_3            5
96 #define GAME_PANEL_INVENTORY_FIRST_4            6
97 #define GAME_PANEL_INVENTORY_FIRST_5            7
98 #define GAME_PANEL_INVENTORY_FIRST_6            8
99 #define GAME_PANEL_INVENTORY_FIRST_7            9
100 #define GAME_PANEL_INVENTORY_FIRST_8            10
101 #define GAME_PANEL_INVENTORY_LAST_1             11
102 #define GAME_PANEL_INVENTORY_LAST_2             12
103 #define GAME_PANEL_INVENTORY_LAST_3             13
104 #define GAME_PANEL_INVENTORY_LAST_4             14
105 #define GAME_PANEL_INVENTORY_LAST_5             15
106 #define GAME_PANEL_INVENTORY_LAST_6             16
107 #define GAME_PANEL_INVENTORY_LAST_7             17
108 #define GAME_PANEL_INVENTORY_LAST_8             18
109 #define GAME_PANEL_KEY_1                        19
110 #define GAME_PANEL_KEY_2                        20
111 #define GAME_PANEL_KEY_3                        21
112 #define GAME_PANEL_KEY_4                        22
113 #define GAME_PANEL_KEY_5                        23
114 #define GAME_PANEL_KEY_6                        24
115 #define GAME_PANEL_KEY_7                        25
116 #define GAME_PANEL_KEY_8                        26
117 #define GAME_PANEL_KEY_WHITE                    27
118 #define GAME_PANEL_KEY_WHITE_COUNT              28
119 #define GAME_PANEL_SCORE                        29
120 #define GAME_PANEL_HIGHSCORE                    30
121 #define GAME_PANEL_TIME                         31
122 #define GAME_PANEL_TIME_HH                      32
123 #define GAME_PANEL_TIME_MM                      33
124 #define GAME_PANEL_TIME_SS                      34
125 #define GAME_PANEL_TIME_ANIM                    35
126 #define GAME_PANEL_HEALTH                       36
127 #define GAME_PANEL_HEALTH_ANIM                  37
128 #define GAME_PANEL_FRAME                        38
129 #define GAME_PANEL_SHIELD_NORMAL                39
130 #define GAME_PANEL_SHIELD_NORMAL_TIME           40
131 #define GAME_PANEL_SHIELD_DEADLY                41
132 #define GAME_PANEL_SHIELD_DEADLY_TIME           42
133 #define GAME_PANEL_EXIT                         43
134 #define GAME_PANEL_EMC_MAGIC_BALL               44
135 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        45
136 #define GAME_PANEL_LIGHT_SWITCH                 46
137 #define GAME_PANEL_LIGHT_SWITCH_TIME            47
138 #define GAME_PANEL_TIMEGATE_SWITCH              48
139 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         49
140 #define GAME_PANEL_SWITCHGATE_SWITCH            50
141 #define GAME_PANEL_EMC_LENSES                   51
142 #define GAME_PANEL_EMC_LENSES_TIME              52
143 #define GAME_PANEL_EMC_MAGNIFIER                53
144 #define GAME_PANEL_EMC_MAGNIFIER_TIME           54
145 #define GAME_PANEL_BALLOON_SWITCH               55
146 #define GAME_PANEL_DYNABOMB_NUMBER              56
147 #define GAME_PANEL_DYNABOMB_SIZE                57
148 #define GAME_PANEL_DYNABOMB_POWER               58
149 #define GAME_PANEL_PENGUINS                     59
150 #define GAME_PANEL_SOKOBAN_OBJECTS              60
151 #define GAME_PANEL_SOKOBAN_FIELDS               61
152 #define GAME_PANEL_ROBOT_WHEEL                  62
153 #define GAME_PANEL_CONVEYOR_BELT_1              63
154 #define GAME_PANEL_CONVEYOR_BELT_2              64
155 #define GAME_PANEL_CONVEYOR_BELT_3              65
156 #define GAME_PANEL_CONVEYOR_BELT_4              66
157 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       67
158 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       68
159 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       69
160 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       70
161 #define GAME_PANEL_MAGIC_WALL                   71
162 #define GAME_PANEL_MAGIC_WALL_TIME              72
163 #define GAME_PANEL_GRAVITY_STATE                73
164 #define GAME_PANEL_GRAPHIC_1                    74
165 #define GAME_PANEL_GRAPHIC_2                    75
166 #define GAME_PANEL_GRAPHIC_3                    76
167 #define GAME_PANEL_GRAPHIC_4                    77
168 #define GAME_PANEL_GRAPHIC_5                    78
169 #define GAME_PANEL_GRAPHIC_6                    79
170 #define GAME_PANEL_GRAPHIC_7                    80
171 #define GAME_PANEL_GRAPHIC_8                    81
172 #define GAME_PANEL_ELEMENT_1                    82
173 #define GAME_PANEL_ELEMENT_2                    83
174 #define GAME_PANEL_ELEMENT_3                    84
175 #define GAME_PANEL_ELEMENT_4                    85
176 #define GAME_PANEL_ELEMENT_5                    86
177 #define GAME_PANEL_ELEMENT_6                    87
178 #define GAME_PANEL_ELEMENT_7                    88
179 #define GAME_PANEL_ELEMENT_8                    89
180 #define GAME_PANEL_ELEMENT_COUNT_1              90
181 #define GAME_PANEL_ELEMENT_COUNT_2              91
182 #define GAME_PANEL_ELEMENT_COUNT_3              92
183 #define GAME_PANEL_ELEMENT_COUNT_4              93
184 #define GAME_PANEL_ELEMENT_COUNT_5              94
185 #define GAME_PANEL_ELEMENT_COUNT_6              95
186 #define GAME_PANEL_ELEMENT_COUNT_7              96
187 #define GAME_PANEL_ELEMENT_COUNT_8              97
188 #define GAME_PANEL_CE_SCORE_1                   98
189 #define GAME_PANEL_CE_SCORE_2                   99
190 #define GAME_PANEL_CE_SCORE_3                   100
191 #define GAME_PANEL_CE_SCORE_4                   101
192 #define GAME_PANEL_CE_SCORE_5                   102
193 #define GAME_PANEL_CE_SCORE_6                   103
194 #define GAME_PANEL_CE_SCORE_7                   104
195 #define GAME_PANEL_CE_SCORE_8                   105
196 #define GAME_PANEL_CE_SCORE_1_ELEMENT           106
197 #define GAME_PANEL_CE_SCORE_2_ELEMENT           107
198 #define GAME_PANEL_CE_SCORE_3_ELEMENT           108
199 #define GAME_PANEL_CE_SCORE_4_ELEMENT           109
200 #define GAME_PANEL_CE_SCORE_5_ELEMENT           110
201 #define GAME_PANEL_CE_SCORE_6_ELEMENT           111
202 #define GAME_PANEL_CE_SCORE_7_ELEMENT           112
203 #define GAME_PANEL_CE_SCORE_8_ELEMENT           113
204 #define GAME_PANEL_PLAYER_NAME                  114
205 #define GAME_PANEL_LEVEL_NAME                   115
206 #define GAME_PANEL_LEVEL_AUTHOR                 116
207
208 #define NUM_GAME_PANEL_CONTROLS                 117
209
210 struct GamePanelOrderInfo
211 {
212   int nr;
213   int sort_priority;
214 };
215
216 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
217
218 struct GamePanelControlInfo
219 {
220   int nr;
221
222   struct TextPosInfo *pos;
223   int type;
224
225   int graphic, graphic_active;
226
227   int value, last_value;
228   int frame, last_frame;
229   int gfx_frame;
230   int gfx_random;
231 };
232
233 static struct GamePanelControlInfo game_panel_controls[] =
234 {
235   {
236     GAME_PANEL_LEVEL_NUMBER,
237     &game.panel.level_number,
238     TYPE_INTEGER,
239   },
240   {
241     GAME_PANEL_GEMS,
242     &game.panel.gems,
243     TYPE_INTEGER,
244   },
245   {
246     GAME_PANEL_INVENTORY_COUNT,
247     &game.panel.inventory_count,
248     TYPE_INTEGER,
249   },
250   {
251     GAME_PANEL_INVENTORY_FIRST_1,
252     &game.panel.inventory_first[0],
253     TYPE_ELEMENT,
254   },
255   {
256     GAME_PANEL_INVENTORY_FIRST_2,
257     &game.panel.inventory_first[1],
258     TYPE_ELEMENT,
259   },
260   {
261     GAME_PANEL_INVENTORY_FIRST_3,
262     &game.panel.inventory_first[2],
263     TYPE_ELEMENT,
264   },
265   {
266     GAME_PANEL_INVENTORY_FIRST_4,
267     &game.panel.inventory_first[3],
268     TYPE_ELEMENT,
269   },
270   {
271     GAME_PANEL_INVENTORY_FIRST_5,
272     &game.panel.inventory_first[4],
273     TYPE_ELEMENT,
274   },
275   {
276     GAME_PANEL_INVENTORY_FIRST_6,
277     &game.panel.inventory_first[5],
278     TYPE_ELEMENT,
279   },
280   {
281     GAME_PANEL_INVENTORY_FIRST_7,
282     &game.panel.inventory_first[6],
283     TYPE_ELEMENT,
284   },
285   {
286     GAME_PANEL_INVENTORY_FIRST_8,
287     &game.panel.inventory_first[7],
288     TYPE_ELEMENT,
289   },
290   {
291     GAME_PANEL_INVENTORY_LAST_1,
292     &game.panel.inventory_last[0],
293     TYPE_ELEMENT,
294   },
295   {
296     GAME_PANEL_INVENTORY_LAST_2,
297     &game.panel.inventory_last[1],
298     TYPE_ELEMENT,
299   },
300   {
301     GAME_PANEL_INVENTORY_LAST_3,
302     &game.panel.inventory_last[2],
303     TYPE_ELEMENT,
304   },
305   {
306     GAME_PANEL_INVENTORY_LAST_4,
307     &game.panel.inventory_last[3],
308     TYPE_ELEMENT,
309   },
310   {
311     GAME_PANEL_INVENTORY_LAST_5,
312     &game.panel.inventory_last[4],
313     TYPE_ELEMENT,
314   },
315   {
316     GAME_PANEL_INVENTORY_LAST_6,
317     &game.panel.inventory_last[5],
318     TYPE_ELEMENT,
319   },
320   {
321     GAME_PANEL_INVENTORY_LAST_7,
322     &game.panel.inventory_last[6],
323     TYPE_ELEMENT,
324   },
325   {
326     GAME_PANEL_INVENTORY_LAST_8,
327     &game.panel.inventory_last[7],
328     TYPE_ELEMENT,
329   },
330   {
331     GAME_PANEL_KEY_1,
332     &game.panel.key[0],
333     TYPE_ELEMENT,
334   },
335   {
336     GAME_PANEL_KEY_2,
337     &game.panel.key[1],
338     TYPE_ELEMENT,
339   },
340   {
341     GAME_PANEL_KEY_3,
342     &game.panel.key[2],
343     TYPE_ELEMENT,
344   },
345   {
346     GAME_PANEL_KEY_4,
347     &game.panel.key[3],
348     TYPE_ELEMENT,
349   },
350   {
351     GAME_PANEL_KEY_5,
352     &game.panel.key[4],
353     TYPE_ELEMENT,
354   },
355   {
356     GAME_PANEL_KEY_6,
357     &game.panel.key[5],
358     TYPE_ELEMENT,
359   },
360   {
361     GAME_PANEL_KEY_7,
362     &game.panel.key[6],
363     TYPE_ELEMENT,
364   },
365   {
366     GAME_PANEL_KEY_8,
367     &game.panel.key[7],
368     TYPE_ELEMENT,
369   },
370   {
371     GAME_PANEL_KEY_WHITE,
372     &game.panel.key_white,
373     TYPE_ELEMENT,
374   },
375   {
376     GAME_PANEL_KEY_WHITE_COUNT,
377     &game.panel.key_white_count,
378     TYPE_INTEGER,
379   },
380   {
381     GAME_PANEL_SCORE,
382     &game.panel.score,
383     TYPE_INTEGER,
384   },
385   {
386     GAME_PANEL_HIGHSCORE,
387     &game.panel.highscore,
388     TYPE_INTEGER,
389   },
390   {
391     GAME_PANEL_TIME,
392     &game.panel.time,
393     TYPE_INTEGER,
394   },
395   {
396     GAME_PANEL_TIME_HH,
397     &game.panel.time_hh,
398     TYPE_INTEGER,
399   },
400   {
401     GAME_PANEL_TIME_MM,
402     &game.panel.time_mm,
403     TYPE_INTEGER,
404   },
405   {
406     GAME_PANEL_TIME_SS,
407     &game.panel.time_ss,
408     TYPE_INTEGER,
409   },
410   {
411     GAME_PANEL_TIME_ANIM,
412     &game.panel.time_anim,
413     TYPE_GRAPHIC,
414
415     IMG_GFX_GAME_PANEL_TIME_ANIM,
416     IMG_GFX_GAME_PANEL_TIME_ANIM_ACTIVE
417   },
418   {
419     GAME_PANEL_HEALTH,
420     &game.panel.health,
421     TYPE_INTEGER,
422   },
423   {
424     GAME_PANEL_HEALTH_ANIM,
425     &game.panel.health_anim,
426     TYPE_GRAPHIC,
427
428     IMG_GFX_GAME_PANEL_HEALTH_ANIM,
429     IMG_GFX_GAME_PANEL_HEALTH_ANIM_ACTIVE
430   },
431   {
432     GAME_PANEL_FRAME,
433     &game.panel.frame,
434     TYPE_INTEGER,
435   },
436   {
437     GAME_PANEL_SHIELD_NORMAL,
438     &game.panel.shield_normal,
439     TYPE_ELEMENT,
440   },
441   {
442     GAME_PANEL_SHIELD_NORMAL_TIME,
443     &game.panel.shield_normal_time,
444     TYPE_INTEGER,
445   },
446   {
447     GAME_PANEL_SHIELD_DEADLY,
448     &game.panel.shield_deadly,
449     TYPE_ELEMENT,
450   },
451   {
452     GAME_PANEL_SHIELD_DEADLY_TIME,
453     &game.panel.shield_deadly_time,
454     TYPE_INTEGER,
455   },
456   {
457     GAME_PANEL_EXIT,
458     &game.panel.exit,
459     TYPE_ELEMENT,
460   },
461   {
462     GAME_PANEL_EMC_MAGIC_BALL,
463     &game.panel.emc_magic_ball,
464     TYPE_ELEMENT,
465   },
466   {
467     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
468     &game.panel.emc_magic_ball_switch,
469     TYPE_ELEMENT,
470   },
471   {
472     GAME_PANEL_LIGHT_SWITCH,
473     &game.panel.light_switch,
474     TYPE_ELEMENT,
475   },
476   {
477     GAME_PANEL_LIGHT_SWITCH_TIME,
478     &game.panel.light_switch_time,
479     TYPE_INTEGER,
480   },
481   {
482     GAME_PANEL_TIMEGATE_SWITCH,
483     &game.panel.timegate_switch,
484     TYPE_ELEMENT,
485   },
486   {
487     GAME_PANEL_TIMEGATE_SWITCH_TIME,
488     &game.panel.timegate_switch_time,
489     TYPE_INTEGER,
490   },
491   {
492     GAME_PANEL_SWITCHGATE_SWITCH,
493     &game.panel.switchgate_switch,
494     TYPE_ELEMENT,
495   },
496   {
497     GAME_PANEL_EMC_LENSES,
498     &game.panel.emc_lenses,
499     TYPE_ELEMENT,
500   },
501   {
502     GAME_PANEL_EMC_LENSES_TIME,
503     &game.panel.emc_lenses_time,
504     TYPE_INTEGER,
505   },
506   {
507     GAME_PANEL_EMC_MAGNIFIER,
508     &game.panel.emc_magnifier,
509     TYPE_ELEMENT,
510   },
511   {
512     GAME_PANEL_EMC_MAGNIFIER_TIME,
513     &game.panel.emc_magnifier_time,
514     TYPE_INTEGER,
515   },
516   {
517     GAME_PANEL_BALLOON_SWITCH,
518     &game.panel.balloon_switch,
519     TYPE_ELEMENT,
520   },
521   {
522     GAME_PANEL_DYNABOMB_NUMBER,
523     &game.panel.dynabomb_number,
524     TYPE_INTEGER,
525   },
526   {
527     GAME_PANEL_DYNABOMB_SIZE,
528     &game.panel.dynabomb_size,
529     TYPE_INTEGER,
530   },
531   {
532     GAME_PANEL_DYNABOMB_POWER,
533     &game.panel.dynabomb_power,
534     TYPE_ELEMENT,
535   },
536   {
537     GAME_PANEL_PENGUINS,
538     &game.panel.penguins,
539     TYPE_INTEGER,
540   },
541   {
542     GAME_PANEL_SOKOBAN_OBJECTS,
543     &game.panel.sokoban_objects,
544     TYPE_INTEGER,
545   },
546   {
547     GAME_PANEL_SOKOBAN_FIELDS,
548     &game.panel.sokoban_fields,
549     TYPE_INTEGER,
550   },
551   {
552     GAME_PANEL_ROBOT_WHEEL,
553     &game.panel.robot_wheel,
554     TYPE_ELEMENT,
555   },
556   {
557     GAME_PANEL_CONVEYOR_BELT_1,
558     &game.panel.conveyor_belt[0],
559     TYPE_ELEMENT,
560   },
561   {
562     GAME_PANEL_CONVEYOR_BELT_2,
563     &game.panel.conveyor_belt[1],
564     TYPE_ELEMENT,
565   },
566   {
567     GAME_PANEL_CONVEYOR_BELT_3,
568     &game.panel.conveyor_belt[2],
569     TYPE_ELEMENT,
570   },
571   {
572     GAME_PANEL_CONVEYOR_BELT_4,
573     &game.panel.conveyor_belt[3],
574     TYPE_ELEMENT,
575   },
576   {
577     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
578     &game.panel.conveyor_belt_switch[0],
579     TYPE_ELEMENT,
580   },
581   {
582     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
583     &game.panel.conveyor_belt_switch[1],
584     TYPE_ELEMENT,
585   },
586   {
587     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
588     &game.panel.conveyor_belt_switch[2],
589     TYPE_ELEMENT,
590   },
591   {
592     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
593     &game.panel.conveyor_belt_switch[3],
594     TYPE_ELEMENT,
595   },
596   {
597     GAME_PANEL_MAGIC_WALL,
598     &game.panel.magic_wall,
599     TYPE_ELEMENT,
600   },
601   {
602     GAME_PANEL_MAGIC_WALL_TIME,
603     &game.panel.magic_wall_time,
604     TYPE_INTEGER,
605   },
606   {
607     GAME_PANEL_GRAVITY_STATE,
608     &game.panel.gravity_state,
609     TYPE_STRING,
610   },
611   {
612     GAME_PANEL_GRAPHIC_1,
613     &game.panel.graphic[0],
614     TYPE_ELEMENT,
615   },
616   {
617     GAME_PANEL_GRAPHIC_2,
618     &game.panel.graphic[1],
619     TYPE_ELEMENT,
620   },
621   {
622     GAME_PANEL_GRAPHIC_3,
623     &game.panel.graphic[2],
624     TYPE_ELEMENT,
625   },
626   {
627     GAME_PANEL_GRAPHIC_4,
628     &game.panel.graphic[3],
629     TYPE_ELEMENT,
630   },
631   {
632     GAME_PANEL_GRAPHIC_5,
633     &game.panel.graphic[4],
634     TYPE_ELEMENT,
635   },
636   {
637     GAME_PANEL_GRAPHIC_6,
638     &game.panel.graphic[5],
639     TYPE_ELEMENT,
640   },
641   {
642     GAME_PANEL_GRAPHIC_7,
643     &game.panel.graphic[6],
644     TYPE_ELEMENT,
645   },
646   {
647     GAME_PANEL_GRAPHIC_8,
648     &game.panel.graphic[7],
649     TYPE_ELEMENT,
650   },
651   {
652     GAME_PANEL_ELEMENT_1,
653     &game.panel.element[0],
654     TYPE_ELEMENT,
655   },
656   {
657     GAME_PANEL_ELEMENT_2,
658     &game.panel.element[1],
659     TYPE_ELEMENT,
660   },
661   {
662     GAME_PANEL_ELEMENT_3,
663     &game.panel.element[2],
664     TYPE_ELEMENT,
665   },
666   {
667     GAME_PANEL_ELEMENT_4,
668     &game.panel.element[3],
669     TYPE_ELEMENT,
670   },
671   {
672     GAME_PANEL_ELEMENT_5,
673     &game.panel.element[4],
674     TYPE_ELEMENT,
675   },
676   {
677     GAME_PANEL_ELEMENT_6,
678     &game.panel.element[5],
679     TYPE_ELEMENT,
680   },
681   {
682     GAME_PANEL_ELEMENT_7,
683     &game.panel.element[6],
684     TYPE_ELEMENT,
685   },
686   {
687     GAME_PANEL_ELEMENT_8,
688     &game.panel.element[7],
689     TYPE_ELEMENT,
690   },
691   {
692     GAME_PANEL_ELEMENT_COUNT_1,
693     &game.panel.element_count[0],
694     TYPE_INTEGER,
695   },
696   {
697     GAME_PANEL_ELEMENT_COUNT_2,
698     &game.panel.element_count[1],
699     TYPE_INTEGER,
700   },
701   {
702     GAME_PANEL_ELEMENT_COUNT_3,
703     &game.panel.element_count[2],
704     TYPE_INTEGER,
705   },
706   {
707     GAME_PANEL_ELEMENT_COUNT_4,
708     &game.panel.element_count[3],
709     TYPE_INTEGER,
710   },
711   {
712     GAME_PANEL_ELEMENT_COUNT_5,
713     &game.panel.element_count[4],
714     TYPE_INTEGER,
715   },
716   {
717     GAME_PANEL_ELEMENT_COUNT_6,
718     &game.panel.element_count[5],
719     TYPE_INTEGER,
720   },
721   {
722     GAME_PANEL_ELEMENT_COUNT_7,
723     &game.panel.element_count[6],
724     TYPE_INTEGER,
725   },
726   {
727     GAME_PANEL_ELEMENT_COUNT_8,
728     &game.panel.element_count[7],
729     TYPE_INTEGER,
730   },
731   {
732     GAME_PANEL_CE_SCORE_1,
733     &game.panel.ce_score[0],
734     TYPE_INTEGER,
735   },
736   {
737     GAME_PANEL_CE_SCORE_2,
738     &game.panel.ce_score[1],
739     TYPE_INTEGER,
740   },
741   {
742     GAME_PANEL_CE_SCORE_3,
743     &game.panel.ce_score[2],
744     TYPE_INTEGER,
745   },
746   {
747     GAME_PANEL_CE_SCORE_4,
748     &game.panel.ce_score[3],
749     TYPE_INTEGER,
750   },
751   {
752     GAME_PANEL_CE_SCORE_5,
753     &game.panel.ce_score[4],
754     TYPE_INTEGER,
755   },
756   {
757     GAME_PANEL_CE_SCORE_6,
758     &game.panel.ce_score[5],
759     TYPE_INTEGER,
760   },
761   {
762     GAME_PANEL_CE_SCORE_7,
763     &game.panel.ce_score[6],
764     TYPE_INTEGER,
765   },
766   {
767     GAME_PANEL_CE_SCORE_8,
768     &game.panel.ce_score[7],
769     TYPE_INTEGER,
770   },
771   {
772     GAME_PANEL_CE_SCORE_1_ELEMENT,
773     &game.panel.ce_score_element[0],
774     TYPE_ELEMENT,
775   },
776   {
777     GAME_PANEL_CE_SCORE_2_ELEMENT,
778     &game.panel.ce_score_element[1],
779     TYPE_ELEMENT,
780   },
781   {
782     GAME_PANEL_CE_SCORE_3_ELEMENT,
783     &game.panel.ce_score_element[2],
784     TYPE_ELEMENT,
785   },
786   {
787     GAME_PANEL_CE_SCORE_4_ELEMENT,
788     &game.panel.ce_score_element[3],
789     TYPE_ELEMENT,
790   },
791   {
792     GAME_PANEL_CE_SCORE_5_ELEMENT,
793     &game.panel.ce_score_element[4],
794     TYPE_ELEMENT,
795   },
796   {
797     GAME_PANEL_CE_SCORE_6_ELEMENT,
798     &game.panel.ce_score_element[5],
799     TYPE_ELEMENT,
800   },
801   {
802     GAME_PANEL_CE_SCORE_7_ELEMENT,
803     &game.panel.ce_score_element[6],
804     TYPE_ELEMENT,
805   },
806   {
807     GAME_PANEL_CE_SCORE_8_ELEMENT,
808     &game.panel.ce_score_element[7],
809     TYPE_ELEMENT,
810   },
811   {
812     GAME_PANEL_PLAYER_NAME,
813     &game.panel.player_name,
814     TYPE_STRING,
815   },
816   {
817     GAME_PANEL_LEVEL_NAME,
818     &game.panel.level_name,
819     TYPE_STRING,
820   },
821   {
822     GAME_PANEL_LEVEL_AUTHOR,
823     &game.panel.level_author,
824     TYPE_STRING,
825   },
826
827   {
828     -1,
829     NULL,
830     -1,
831   }
832 };
833
834 // values for delayed check of falling and moving elements and for collision
835 #define CHECK_DELAY_MOVING      3
836 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
837 #define CHECK_DELAY_COLLISION   2
838 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
839
840 // values for initial player move delay (initial delay counter value)
841 #define INITIAL_MOVE_DELAY_OFF  -1
842 #define INITIAL_MOVE_DELAY_ON   0
843
844 // values for player movement speed (which is in fact a delay value)
845 #define MOVE_DELAY_MIN_SPEED    32
846 #define MOVE_DELAY_NORMAL_SPEED 8
847 #define MOVE_DELAY_HIGH_SPEED   4
848 #define MOVE_DELAY_MAX_SPEED    1
849
850 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
851 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
852
853 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
854 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
855
856 // values for scroll positions
857 #define SCROLL_POSITION_X(x)    ((x) < SBX_Left  + MIDPOSX ? SBX_Left : \
858                                  (x) > SBX_Right + MIDPOSX ? SBX_Right :\
859                                  (x) - MIDPOSX)
860 #define SCROLL_POSITION_Y(y)    ((y) < SBY_Upper + MIDPOSY ? SBY_Upper :\
861                                  (y) > SBY_Lower + MIDPOSY ? SBY_Lower :\
862                                  (y) - MIDPOSY)
863
864 // values for other actions
865 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
866 #define MOVE_STEPSIZE_MIN       (1)
867 #define MOVE_STEPSIZE_MAX       (TILEX)
868
869 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
870 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
871
872 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
873
874 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
875                                  RND(element_info[e].push_delay_random))
876 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
877                                  RND(element_info[e].drop_delay_random))
878 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
879                                  RND(element_info[e].move_delay_random))
880 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
881                                     (element_info[e].move_delay_random))
882 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
883                                  RND(element_info[e].ce_value_random_initial))
884 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
885 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
886                                  RND((c)->delay_random * (c)->delay_frames))
887 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
888                                  RND((c)->delay_random))
889
890
891 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
892          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
893
894 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
895         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
896          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
897          (be) + (e) - EL_SELF)
898
899 #define GET_PLAYER_FROM_BITS(p)                                         \
900         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
901
902 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
903         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
904          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
905          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
906          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
907          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
908          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
909          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
910          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
911          (e))
912
913 #define CAN_GROW_INTO(e)                                                \
914         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
915
916 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
917                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
918                                         (condition)))
919
920 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
921                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
922                                         (CAN_MOVE_INTO_ACID(e) &&       \
923                                          Feld[x][y] == EL_ACID) ||      \
924                                         (condition)))
925
926 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
927                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
928                                         (CAN_MOVE_INTO_ACID(e) &&       \
929                                          Feld[x][y] == EL_ACID) ||      \
930                                         (condition)))
931
932 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
933                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
934                                         (condition) ||                  \
935                                         (CAN_MOVE_INTO_ACID(e) &&       \
936                                          Feld[x][y] == EL_ACID) ||      \
937                                         (DONT_COLLIDE_WITH(e) &&        \
938                                          IS_PLAYER(x, y) &&             \
939                                          !PLAYER_ENEMY_PROTECTED(x, y))))
940
941 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
942         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
943
944 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
945         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
946
947 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
948         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
949
950 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
951         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
952                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
953
954 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
955         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
956
957 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
958         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
959
960 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
961         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
962
963 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
964         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
965
966 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
967         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
968
969 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
970         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
971                                                  Feld[x][y] == EL_EM_EXIT_OPEN || \
972                                                  Feld[x][y] == EL_STEEL_EXIT_OPEN || \
973                                                  Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
974                                                  IS_FOOD_PENGUIN(Feld[x][y])))
975 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
976         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
977
978 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
979         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
980
981 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
982         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
983
984 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
985         (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER ||   \
986                                 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
987
988 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
989
990 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
991                 (!IS_PLAYER(x, y) &&                                    \
992                  IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
993
994 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
995         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
996
997 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
998 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
999
1000 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
1001 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
1002 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
1003 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1004
1005 #define MM_HEALTH(x)            (MIN(MAX(0, MAX_HEALTH - (x)), MAX_HEALTH))
1006
1007 // game button identifiers
1008 #define GAME_CTRL_ID_STOP               0
1009 #define GAME_CTRL_ID_PAUSE              1
1010 #define GAME_CTRL_ID_PLAY               2
1011 #define GAME_CTRL_ID_UNDO               3
1012 #define GAME_CTRL_ID_REDO               4
1013 #define GAME_CTRL_ID_SAVE               5
1014 #define GAME_CTRL_ID_PAUSE2             6
1015 #define GAME_CTRL_ID_LOAD               7
1016 #define GAME_CTRL_ID_PANEL_STOP         8
1017 #define GAME_CTRL_ID_PANEL_PAUSE        9
1018 #define GAME_CTRL_ID_PANEL_PLAY         10
1019 #define SOUND_CTRL_ID_MUSIC             11
1020 #define SOUND_CTRL_ID_LOOPS             12
1021 #define SOUND_CTRL_ID_SIMPLE            13
1022 #define SOUND_CTRL_ID_PANEL_MUSIC       14
1023 #define SOUND_CTRL_ID_PANEL_LOOPS       15
1024 #define SOUND_CTRL_ID_PANEL_SIMPLE      16
1025
1026 #define NUM_GAME_BUTTONS                17
1027
1028
1029 // forward declaration for internal use
1030
1031 static void CreateField(int, int, int);
1032
1033 static void ResetGfxAnimation(int, int);
1034
1035 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1036 static void AdvanceFrameAndPlayerCounters(int);
1037
1038 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1039 static boolean MovePlayer(struct PlayerInfo *, int, int);
1040 static void ScrollPlayer(struct PlayerInfo *, int);
1041 static void ScrollScreen(struct PlayerInfo *, int);
1042
1043 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1044 static boolean DigFieldByCE(int, int, int);
1045 static boolean SnapField(struct PlayerInfo *, int, int);
1046 static boolean DropElement(struct PlayerInfo *);
1047
1048 static void InitBeltMovement(void);
1049 static void CloseAllOpenTimegates(void);
1050 static void CheckGravityMovement(struct PlayerInfo *);
1051 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1052 static void KillPlayerUnlessEnemyProtected(int, int);
1053 static void KillPlayerUnlessExplosionProtected(int, int);
1054
1055 static void TestIfPlayerTouchesCustomElement(int, int);
1056 static void TestIfElementTouchesCustomElement(int, int);
1057 static void TestIfElementHitsCustomElement(int, int, int);
1058
1059 static void HandleElementChange(int, int, int);
1060 static void ExecuteCustomElementAction(int, int, int, int);
1061 static boolean ChangeElement(int, int, int, int);
1062
1063 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1064 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1065         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1066 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1067         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1068 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1069         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1070 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1071         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1072
1073 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1074 #define CheckElementChange(x, y, e, te, ev)                             \
1075         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1076 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1077         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1078 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1079         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1080
1081 static void PlayLevelSound(int, int, int);
1082 static void PlayLevelSoundNearest(int, int, int);
1083 static void PlayLevelSoundAction(int, int, int);
1084 static void PlayLevelSoundElementAction(int, int, int, int);
1085 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1086 static void PlayLevelSoundActionIfLoop(int, int, int);
1087 static void StopLevelSoundActionIfLoop(int, int, int);
1088 static void PlayLevelMusic(void);
1089 static void FadeLevelSoundsAndMusic(void);
1090
1091 static void HandleGameButtons(struct GadgetInfo *);
1092
1093 int AmoebeNachbarNr(int, int);
1094 void AmoebeUmwandeln(int, int);
1095 void ContinueMoving(int, int);
1096 void Bang(int, int);
1097 void InitMovDir(int, int);
1098 void InitAmoebaNr(int, int);
1099 int NewHiScore(int);
1100
1101 void TestIfGoodThingHitsBadThing(int, int, int);
1102 void TestIfBadThingHitsGoodThing(int, int, int);
1103 void TestIfPlayerTouchesBadThing(int, int);
1104 void TestIfPlayerRunsIntoBadThing(int, int, int);
1105 void TestIfBadThingTouchesPlayer(int, int);
1106 void TestIfBadThingRunsIntoPlayer(int, int, int);
1107 void TestIfFriendTouchesBadThing(int, int);
1108 void TestIfBadThingTouchesFriend(int, int);
1109 void TestIfBadThingTouchesOtherBadThing(int, int);
1110 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1111
1112 void KillPlayer(struct PlayerInfo *);
1113 void BuryPlayer(struct PlayerInfo *);
1114 void RemovePlayer(struct PlayerInfo *);
1115 void ExitPlayer(struct PlayerInfo *);
1116
1117 static int getInvisibleActiveFromInvisibleElement(int);
1118 static int getInvisibleFromInvisibleActiveElement(int);
1119
1120 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1121
1122 // for detection of endless loops, caused by custom element programming
1123 // (using maximal playfield width x 10 is just a rough approximation)
1124 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1125
1126 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1127 {                                                                       \
1128   if (recursion_loop_detected)                                          \
1129     return (rc);                                                        \
1130                                                                         \
1131   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1132   {                                                                     \
1133     recursion_loop_detected = TRUE;                                     \
1134     recursion_loop_element = (e);                                       \
1135   }                                                                     \
1136                                                                         \
1137   recursion_loop_depth++;                                               \
1138 }
1139
1140 #define RECURSION_LOOP_DETECTION_END()                                  \
1141 {                                                                       \
1142   recursion_loop_depth--;                                               \
1143 }
1144
1145 static int recursion_loop_depth;
1146 static boolean recursion_loop_detected;
1147 static boolean recursion_loop_element;
1148
1149 static int map_player_action[MAX_PLAYERS];
1150
1151
1152 // ----------------------------------------------------------------------------
1153 // definition of elements that automatically change to other elements after
1154 // a specified time, eventually calling a function when changing
1155 // ----------------------------------------------------------------------------
1156
1157 // forward declaration for changer functions
1158 static void InitBuggyBase(int, int);
1159 static void WarnBuggyBase(int, int);
1160
1161 static void InitTrap(int, int);
1162 static void ActivateTrap(int, int);
1163 static void ChangeActiveTrap(int, int);
1164
1165 static void InitRobotWheel(int, int);
1166 static void RunRobotWheel(int, int);
1167 static void StopRobotWheel(int, int);
1168
1169 static void InitTimegateWheel(int, int);
1170 static void RunTimegateWheel(int, int);
1171
1172 static void InitMagicBallDelay(int, int);
1173 static void ActivateMagicBall(int, int);
1174
1175 struct ChangingElementInfo
1176 {
1177   int element;
1178   int target_element;
1179   int change_delay;
1180   void (*pre_change_function)(int x, int y);
1181   void (*change_function)(int x, int y);
1182   void (*post_change_function)(int x, int y);
1183 };
1184
1185 static struct ChangingElementInfo change_delay_list[] =
1186 {
1187   {
1188     EL_NUT_BREAKING,
1189     EL_EMERALD,
1190     6,
1191     NULL,
1192     NULL,
1193     NULL
1194   },
1195   {
1196     EL_PEARL_BREAKING,
1197     EL_EMPTY,
1198     8,
1199     NULL,
1200     NULL,
1201     NULL
1202   },
1203   {
1204     EL_EXIT_OPENING,
1205     EL_EXIT_OPEN,
1206     29,
1207     NULL,
1208     NULL,
1209     NULL
1210   },
1211   {
1212     EL_EXIT_CLOSING,
1213     EL_EXIT_CLOSED,
1214     29,
1215     NULL,
1216     NULL,
1217     NULL
1218   },
1219   {
1220     EL_STEEL_EXIT_OPENING,
1221     EL_STEEL_EXIT_OPEN,
1222     29,
1223     NULL,
1224     NULL,
1225     NULL
1226   },
1227   {
1228     EL_STEEL_EXIT_CLOSING,
1229     EL_STEEL_EXIT_CLOSED,
1230     29,
1231     NULL,
1232     NULL,
1233     NULL
1234   },
1235   {
1236     EL_EM_EXIT_OPENING,
1237     EL_EM_EXIT_OPEN,
1238     29,
1239     NULL,
1240     NULL,
1241     NULL
1242   },
1243   {
1244     EL_EM_EXIT_CLOSING,
1245     EL_EMPTY,
1246     29,
1247     NULL,
1248     NULL,
1249     NULL
1250   },
1251   {
1252     EL_EM_STEEL_EXIT_OPENING,
1253     EL_EM_STEEL_EXIT_OPEN,
1254     29,
1255     NULL,
1256     NULL,
1257     NULL
1258   },
1259   {
1260     EL_EM_STEEL_EXIT_CLOSING,
1261     EL_STEELWALL,
1262     29,
1263     NULL,
1264     NULL,
1265     NULL
1266   },
1267   {
1268     EL_SP_EXIT_OPENING,
1269     EL_SP_EXIT_OPEN,
1270     29,
1271     NULL,
1272     NULL,
1273     NULL
1274   },
1275   {
1276     EL_SP_EXIT_CLOSING,
1277     EL_SP_EXIT_CLOSED,
1278     29,
1279     NULL,
1280     NULL,
1281     NULL
1282   },
1283   {
1284     EL_SWITCHGATE_OPENING,
1285     EL_SWITCHGATE_OPEN,
1286     29,
1287     NULL,
1288     NULL,
1289     NULL
1290   },
1291   {
1292     EL_SWITCHGATE_CLOSING,
1293     EL_SWITCHGATE_CLOSED,
1294     29,
1295     NULL,
1296     NULL,
1297     NULL
1298   },
1299   {
1300     EL_TIMEGATE_OPENING,
1301     EL_TIMEGATE_OPEN,
1302     29,
1303     NULL,
1304     NULL,
1305     NULL
1306   },
1307   {
1308     EL_TIMEGATE_CLOSING,
1309     EL_TIMEGATE_CLOSED,
1310     29,
1311     NULL,
1312     NULL,
1313     NULL
1314   },
1315
1316   {
1317     EL_ACID_SPLASH_LEFT,
1318     EL_EMPTY,
1319     8,
1320     NULL,
1321     NULL,
1322     NULL
1323   },
1324   {
1325     EL_ACID_SPLASH_RIGHT,
1326     EL_EMPTY,
1327     8,
1328     NULL,
1329     NULL,
1330     NULL
1331   },
1332   {
1333     EL_SP_BUGGY_BASE,
1334     EL_SP_BUGGY_BASE_ACTIVATING,
1335     0,
1336     InitBuggyBase,
1337     NULL,
1338     NULL
1339   },
1340   {
1341     EL_SP_BUGGY_BASE_ACTIVATING,
1342     EL_SP_BUGGY_BASE_ACTIVE,
1343     0,
1344     InitBuggyBase,
1345     NULL,
1346     NULL
1347   },
1348   {
1349     EL_SP_BUGGY_BASE_ACTIVE,
1350     EL_SP_BUGGY_BASE,
1351     0,
1352     InitBuggyBase,
1353     WarnBuggyBase,
1354     NULL
1355   },
1356   {
1357     EL_TRAP,
1358     EL_TRAP_ACTIVE,
1359     0,
1360     InitTrap,
1361     NULL,
1362     ActivateTrap
1363   },
1364   {
1365     EL_TRAP_ACTIVE,
1366     EL_TRAP,
1367     31,
1368     NULL,
1369     ChangeActiveTrap,
1370     NULL
1371   },
1372   {
1373     EL_ROBOT_WHEEL_ACTIVE,
1374     EL_ROBOT_WHEEL,
1375     0,
1376     InitRobotWheel,
1377     RunRobotWheel,
1378     StopRobotWheel
1379   },
1380   {
1381     EL_TIMEGATE_SWITCH_ACTIVE,
1382     EL_TIMEGATE_SWITCH,
1383     0,
1384     InitTimegateWheel,
1385     RunTimegateWheel,
1386     NULL
1387   },
1388   {
1389     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1390     EL_DC_TIMEGATE_SWITCH,
1391     0,
1392     InitTimegateWheel,
1393     RunTimegateWheel,
1394     NULL
1395   },
1396   {
1397     EL_EMC_MAGIC_BALL_ACTIVE,
1398     EL_EMC_MAGIC_BALL_ACTIVE,
1399     0,
1400     InitMagicBallDelay,
1401     NULL,
1402     ActivateMagicBall
1403   },
1404   {
1405     EL_EMC_SPRING_BUMPER_ACTIVE,
1406     EL_EMC_SPRING_BUMPER,
1407     8,
1408     NULL,
1409     NULL,
1410     NULL
1411   },
1412   {
1413     EL_DIAGONAL_SHRINKING,
1414     EL_UNDEFINED,
1415     0,
1416     NULL,
1417     NULL,
1418     NULL
1419   },
1420   {
1421     EL_DIAGONAL_GROWING,
1422     EL_UNDEFINED,
1423     0,
1424     NULL,
1425     NULL,
1426     NULL,
1427   },
1428
1429   {
1430     EL_UNDEFINED,
1431     EL_UNDEFINED,
1432     -1,
1433     NULL,
1434     NULL,
1435     NULL
1436   }
1437 };
1438
1439 struct
1440 {
1441   int element;
1442   int push_delay_fixed, push_delay_random;
1443 }
1444 push_delay_list[] =
1445 {
1446   { EL_SPRING,                  0, 0 },
1447   { EL_BALLOON,                 0, 0 },
1448
1449   { EL_SOKOBAN_OBJECT,          2, 0 },
1450   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1451   { EL_SATELLITE,               2, 0 },
1452   { EL_SP_DISK_YELLOW,          2, 0 },
1453
1454   { EL_UNDEFINED,               0, 0 },
1455 };
1456
1457 struct
1458 {
1459   int element;
1460   int move_stepsize;
1461 }
1462 move_stepsize_list[] =
1463 {
1464   { EL_AMOEBA_DROP,             2 },
1465   { EL_AMOEBA_DROPPING,         2 },
1466   { EL_QUICKSAND_FILLING,       1 },
1467   { EL_QUICKSAND_EMPTYING,      1 },
1468   { EL_QUICKSAND_FAST_FILLING,  2 },
1469   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1470   { EL_MAGIC_WALL_FILLING,      2 },
1471   { EL_MAGIC_WALL_EMPTYING,     2 },
1472   { EL_BD_MAGIC_WALL_FILLING,   2 },
1473   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1474   { EL_DC_MAGIC_WALL_FILLING,   2 },
1475   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1476
1477   { EL_UNDEFINED,               0 },
1478 };
1479
1480 struct
1481 {
1482   int element;
1483   int count;
1484 }
1485 collect_count_list[] =
1486 {
1487   { EL_EMERALD,                 1 },
1488   { EL_BD_DIAMOND,              1 },
1489   { EL_EMERALD_YELLOW,          1 },
1490   { EL_EMERALD_RED,             1 },
1491   { EL_EMERALD_PURPLE,          1 },
1492   { EL_DIAMOND,                 3 },
1493   { EL_SP_INFOTRON,             1 },
1494   { EL_PEARL,                   5 },
1495   { EL_CRYSTAL,                 8 },
1496
1497   { EL_UNDEFINED,               0 },
1498 };
1499
1500 struct
1501 {
1502   int element;
1503   int direction;
1504 }
1505 access_direction_list[] =
1506 {
1507   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1508   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1509   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1510   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1511   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1512   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1513   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1514   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1515   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1516   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1517   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1518
1519   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1520   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1521   { EL_SP_PORT_UP,                                                   MV_DOWN },
1522   { EL_SP_PORT_DOWN,                                         MV_UP           },
1523   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1524   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1525   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1526   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1527   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1528   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1529   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1530   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1531   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1532   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1533   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1534   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1535   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1536   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1537   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1538
1539   { EL_UNDEFINED,                       MV_NONE                              }
1540 };
1541
1542 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1543
1544 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1545 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1546 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1547                                  IS_JUST_CHANGING(x, y))
1548
1549 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1550
1551 // static variables for playfield scan mode (scanning forward or backward)
1552 static int playfield_scan_start_x = 0;
1553 static int playfield_scan_start_y = 0;
1554 static int playfield_scan_delta_x = 1;
1555 static int playfield_scan_delta_y = 1;
1556
1557 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1558                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1559                                      (y) += playfield_scan_delta_y)     \
1560                                 for ((x) = playfield_scan_start_x;      \
1561                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1562                                      (x) += playfield_scan_delta_x)
1563
1564 #ifdef DEBUG
1565 void DEBUG_SetMaximumDynamite(void)
1566 {
1567   int i;
1568
1569   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1570     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1571       local_player->inventory_element[local_player->inventory_size++] =
1572         EL_DYNAMITE;
1573 }
1574 #endif
1575
1576 static void InitPlayfieldScanModeVars(void)
1577 {
1578   if (game.use_reverse_scan_direction)
1579   {
1580     playfield_scan_start_x = lev_fieldx - 1;
1581     playfield_scan_start_y = lev_fieldy - 1;
1582
1583     playfield_scan_delta_x = -1;
1584     playfield_scan_delta_y = -1;
1585   }
1586   else
1587   {
1588     playfield_scan_start_x = 0;
1589     playfield_scan_start_y = 0;
1590
1591     playfield_scan_delta_x = 1;
1592     playfield_scan_delta_y = 1;
1593   }
1594 }
1595
1596 static void InitPlayfieldScanMode(int mode)
1597 {
1598   game.use_reverse_scan_direction =
1599     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1600
1601   InitPlayfieldScanModeVars();
1602 }
1603
1604 static int get_move_delay_from_stepsize(int move_stepsize)
1605 {
1606   move_stepsize =
1607     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1608
1609   // make sure that stepsize value is always a power of 2
1610   move_stepsize = (1 << log_2(move_stepsize));
1611
1612   return TILEX / move_stepsize;
1613 }
1614
1615 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1616                                boolean init_game)
1617 {
1618   int player_nr = player->index_nr;
1619   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1620   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1621
1622   // do no immediately change move delay -- the player might just be moving
1623   player->move_delay_value_next = move_delay;
1624
1625   // information if player can move must be set separately
1626   player->cannot_move = cannot_move;
1627
1628   if (init_game)
1629   {
1630     player->move_delay       = game.initial_move_delay[player_nr];
1631     player->move_delay_value = game.initial_move_delay_value[player_nr];
1632
1633     player->move_delay_value_next = -1;
1634
1635     player->move_delay_reset_counter = 0;
1636   }
1637 }
1638
1639 void GetPlayerConfig(void)
1640 {
1641   GameFrameDelay = setup.game_frame_delay;
1642
1643   if (!audio.sound_available)
1644     setup.sound_simple = FALSE;
1645
1646   if (!audio.loops_available)
1647     setup.sound_loops = FALSE;
1648
1649   if (!audio.music_available)
1650     setup.sound_music = FALSE;
1651
1652   if (!video.fullscreen_available)
1653     setup.fullscreen = FALSE;
1654
1655   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1656
1657   SetAudioMode(setup.sound);
1658 }
1659
1660 int GetElementFromGroupElement(int element)
1661 {
1662   if (IS_GROUP_ELEMENT(element))
1663   {
1664     struct ElementGroupInfo *group = element_info[element].group;
1665     int last_anim_random_frame = gfx.anim_random_frame;
1666     int element_pos;
1667
1668     if (group->choice_mode == ANIM_RANDOM)
1669       gfx.anim_random_frame = RND(group->num_elements_resolved);
1670
1671     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1672                                     group->choice_mode, 0,
1673                                     group->choice_pos);
1674
1675     if (group->choice_mode == ANIM_RANDOM)
1676       gfx.anim_random_frame = last_anim_random_frame;
1677
1678     group->choice_pos++;
1679
1680     element = group->element_resolved[element_pos];
1681   }
1682
1683   return element;
1684 }
1685
1686 static void IncrementPlayerSokobanFieldsNeeded(struct PlayerInfo *player)
1687 {
1688   if (level.sb_fields_needed)
1689     player->sokoban_fields_still_needed++;
1690 }
1691
1692 static void IncrementPlayerSokobanObjectsNeeded(struct PlayerInfo *player)
1693 {
1694   if (level.sb_objects_needed)
1695     player->sokoban_objects_still_needed++;
1696 }
1697
1698 static void DecrementPlayerSokobanFieldsNeeded(struct PlayerInfo *player)
1699 {
1700   if (player->sokoban_fields_still_needed > 0)
1701     player->sokoban_fields_still_needed--;
1702 }
1703
1704 static void DecrementPlayerSokobanObjectsNeeded(struct PlayerInfo *player)
1705 {
1706   if (player->sokoban_objects_still_needed > 0)
1707     player->sokoban_objects_still_needed--;
1708 }
1709
1710 static void InitPlayerField(int x, int y, int element, boolean init_game)
1711 {
1712   if (element == EL_SP_MURPHY)
1713   {
1714     if (init_game)
1715     {
1716       if (stored_player[0].present)
1717       {
1718         Feld[x][y] = EL_SP_MURPHY_CLONE;
1719
1720         return;
1721       }
1722       else
1723       {
1724         stored_player[0].initial_element = element;
1725         stored_player[0].use_murphy = TRUE;
1726
1727         if (!level.use_artwork_element[0])
1728           stored_player[0].artwork_element = EL_SP_MURPHY;
1729       }
1730
1731       Feld[x][y] = EL_PLAYER_1;
1732     }
1733   }
1734
1735   if (init_game)
1736   {
1737     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1738     int jx = player->jx, jy = player->jy;
1739
1740     player->present = TRUE;
1741
1742     player->block_last_field = (element == EL_SP_MURPHY ?
1743                                 level.sp_block_last_field :
1744                                 level.block_last_field);
1745
1746     // ---------- initialize player's last field block delay ------------------
1747
1748     // always start with reliable default value (no adjustment needed)
1749     player->block_delay_adjustment = 0;
1750
1751     // special case 1: in Supaplex, Murphy blocks last field one more frame
1752     if (player->block_last_field && element == EL_SP_MURPHY)
1753       player->block_delay_adjustment = 1;
1754
1755     // special case 2: in game engines before 3.1.1, blocking was different
1756     if (game.use_block_last_field_bug)
1757       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1758
1759     if (!network.enabled || player->connected_network)
1760     {
1761       player->active = TRUE;
1762
1763       // remove potentially duplicate players
1764       if (StorePlayer[jx][jy] == Feld[x][y])
1765         StorePlayer[jx][jy] = 0;
1766
1767       StorePlayer[x][y] = Feld[x][y];
1768
1769 #if DEBUG_INIT_PLAYER
1770       if (options.debug)
1771       {
1772         printf("- player element %d activated", player->element_nr);
1773         printf(" (local player is %d and currently %s)\n",
1774                local_player->element_nr,
1775                local_player->active ? "active" : "not active");
1776       }
1777     }
1778 #endif
1779
1780     Feld[x][y] = EL_EMPTY;
1781
1782     player->jx = player->last_jx = x;
1783     player->jy = player->last_jy = y;
1784   }
1785
1786   if (!init_game)
1787   {
1788     int player_nr = GET_PLAYER_NR(element);
1789     struct PlayerInfo *player = &stored_player[player_nr];
1790
1791     if (player->active && player->killed)
1792       player->reanimated = TRUE; // if player was just killed, reanimate him
1793   }
1794 }
1795
1796 static void InitField(int x, int y, boolean init_game)
1797 {
1798   int element = Feld[x][y];
1799
1800   switch (element)
1801   {
1802     case EL_SP_MURPHY:
1803     case EL_PLAYER_1:
1804     case EL_PLAYER_2:
1805     case EL_PLAYER_3:
1806     case EL_PLAYER_4:
1807       InitPlayerField(x, y, element, init_game);
1808       break;
1809
1810     case EL_SOKOBAN_FIELD_PLAYER:
1811       element = Feld[x][y] = EL_PLAYER_1;
1812       InitField(x, y, init_game);
1813
1814       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1815       InitField(x, y, init_game);
1816       break;
1817
1818     case EL_SOKOBAN_FIELD_EMPTY:
1819       IncrementPlayerSokobanFieldsNeeded(local_player);
1820       break;
1821
1822     case EL_SOKOBAN_OBJECT:
1823       IncrementPlayerSokobanObjectsNeeded(local_player);
1824       break;
1825
1826     case EL_STONEBLOCK:
1827       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1828         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1829       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1830         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1831       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1832         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1833       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1834         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1835       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1836         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1837       break;
1838
1839     case EL_BUG:
1840     case EL_BUG_RIGHT:
1841     case EL_BUG_UP:
1842     case EL_BUG_LEFT:
1843     case EL_BUG_DOWN:
1844     case EL_SPACESHIP:
1845     case EL_SPACESHIP_RIGHT:
1846     case EL_SPACESHIP_UP:
1847     case EL_SPACESHIP_LEFT:
1848     case EL_SPACESHIP_DOWN:
1849     case EL_BD_BUTTERFLY:
1850     case EL_BD_BUTTERFLY_RIGHT:
1851     case EL_BD_BUTTERFLY_UP:
1852     case EL_BD_BUTTERFLY_LEFT:
1853     case EL_BD_BUTTERFLY_DOWN:
1854     case EL_BD_FIREFLY:
1855     case EL_BD_FIREFLY_RIGHT:
1856     case EL_BD_FIREFLY_UP:
1857     case EL_BD_FIREFLY_LEFT:
1858     case EL_BD_FIREFLY_DOWN:
1859     case EL_PACMAN_RIGHT:
1860     case EL_PACMAN_UP:
1861     case EL_PACMAN_LEFT:
1862     case EL_PACMAN_DOWN:
1863     case EL_YAMYAM:
1864     case EL_YAMYAM_LEFT:
1865     case EL_YAMYAM_RIGHT:
1866     case EL_YAMYAM_UP:
1867     case EL_YAMYAM_DOWN:
1868     case EL_DARK_YAMYAM:
1869     case EL_ROBOT:
1870     case EL_PACMAN:
1871     case EL_SP_SNIKSNAK:
1872     case EL_SP_ELECTRON:
1873     case EL_MOLE:
1874     case EL_MOLE_LEFT:
1875     case EL_MOLE_RIGHT:
1876     case EL_MOLE_UP:
1877     case EL_MOLE_DOWN:
1878       InitMovDir(x, y);
1879       break;
1880
1881     case EL_AMOEBA_FULL:
1882     case EL_BD_AMOEBA:
1883       InitAmoebaNr(x, y);
1884       break;
1885
1886     case EL_AMOEBA_DROP:
1887       if (y == lev_fieldy - 1)
1888       {
1889         Feld[x][y] = EL_AMOEBA_GROWING;
1890         Store[x][y] = EL_AMOEBA_WET;
1891       }
1892       break;
1893
1894     case EL_DYNAMITE_ACTIVE:
1895     case EL_SP_DISK_RED_ACTIVE:
1896     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1897     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1898     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1899     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1900       MovDelay[x][y] = 96;
1901       break;
1902
1903     case EL_EM_DYNAMITE_ACTIVE:
1904       MovDelay[x][y] = 32;
1905       break;
1906
1907     case EL_LAMP:
1908       local_player->lights_still_needed++;
1909       break;
1910
1911     case EL_PENGUIN:
1912       local_player->friends_still_needed++;
1913       break;
1914
1915     case EL_PIG:
1916     case EL_DRAGON:
1917       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1918       break;
1919
1920     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1921     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1922     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1923     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1924     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1925     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1926     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1927     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1928     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1929     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1930     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1931     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1932       if (init_game)
1933       {
1934         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1935         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1936         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1937
1938         if (game.belt_dir_nr[belt_nr] == 3)     // initial value
1939         {
1940           game.belt_dir[belt_nr] = belt_dir;
1941           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1942         }
1943         else    // more than one switch -- set it like the first switch
1944         {
1945           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1946         }
1947       }
1948       break;
1949
1950     case EL_LIGHT_SWITCH_ACTIVE:
1951       if (init_game)
1952         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1953       break;
1954
1955     case EL_INVISIBLE_STEELWALL:
1956     case EL_INVISIBLE_WALL:
1957     case EL_INVISIBLE_SAND:
1958       if (game.light_time_left > 0 ||
1959           game.lenses_time_left > 0)
1960         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1961       break;
1962
1963     case EL_EMC_MAGIC_BALL:
1964       if (game.ball_state)
1965         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1966       break;
1967
1968     case EL_EMC_MAGIC_BALL_SWITCH:
1969       if (game.ball_state)
1970         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1971       break;
1972
1973     case EL_TRIGGER_PLAYER:
1974     case EL_TRIGGER_ELEMENT:
1975     case EL_TRIGGER_CE_VALUE:
1976     case EL_TRIGGER_CE_SCORE:
1977     case EL_SELF:
1978     case EL_ANY_ELEMENT:
1979     case EL_CURRENT_CE_VALUE:
1980     case EL_CURRENT_CE_SCORE:
1981     case EL_PREV_CE_1:
1982     case EL_PREV_CE_2:
1983     case EL_PREV_CE_3:
1984     case EL_PREV_CE_4:
1985     case EL_PREV_CE_5:
1986     case EL_PREV_CE_6:
1987     case EL_PREV_CE_7:
1988     case EL_PREV_CE_8:
1989     case EL_NEXT_CE_1:
1990     case EL_NEXT_CE_2:
1991     case EL_NEXT_CE_3:
1992     case EL_NEXT_CE_4:
1993     case EL_NEXT_CE_5:
1994     case EL_NEXT_CE_6:
1995     case EL_NEXT_CE_7:
1996     case EL_NEXT_CE_8:
1997       // reference elements should not be used on the playfield
1998       Feld[x][y] = EL_EMPTY;
1999       break;
2000
2001     default:
2002       if (IS_CUSTOM_ELEMENT(element))
2003       {
2004         if (CAN_MOVE(element))
2005           InitMovDir(x, y);
2006
2007         if (!element_info[element].use_last_ce_value || init_game)
2008           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
2009       }
2010       else if (IS_GROUP_ELEMENT(element))
2011       {
2012         Feld[x][y] = GetElementFromGroupElement(element);
2013
2014         InitField(x, y, init_game);
2015       }
2016
2017       break;
2018   }
2019
2020   if (!init_game)
2021     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2022 }
2023
2024 static void InitField_WithBug1(int x, int y, boolean init_game)
2025 {
2026   InitField(x, y, init_game);
2027
2028   // not needed to call InitMovDir() -- already done by InitField()!
2029   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2030       CAN_MOVE(Feld[x][y]))
2031     InitMovDir(x, y);
2032 }
2033
2034 static void InitField_WithBug2(int x, int y, boolean init_game)
2035 {
2036   int old_element = Feld[x][y];
2037
2038   InitField(x, y, init_game);
2039
2040   // not needed to call InitMovDir() -- already done by InitField()!
2041   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2042       CAN_MOVE(old_element) &&
2043       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2044     InitMovDir(x, y);
2045
2046   /* this case is in fact a combination of not less than three bugs:
2047      first, it calls InitMovDir() for elements that can move, although this is
2048      already done by InitField(); then, it checks the element that was at this
2049      field _before_ the call to InitField() (which can change it); lastly, it
2050      was not called for "mole with direction" elements, which were treated as
2051      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2052   */
2053 }
2054
2055 static int get_key_element_from_nr(int key_nr)
2056 {
2057   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2058                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2059                           EL_EM_KEY_1 : EL_KEY_1);
2060
2061   return key_base_element + key_nr;
2062 }
2063
2064 static int get_next_dropped_element(struct PlayerInfo *player)
2065 {
2066   return (player->inventory_size > 0 ?
2067           player->inventory_element[player->inventory_size - 1] :
2068           player->inventory_infinite_element != EL_UNDEFINED ?
2069           player->inventory_infinite_element :
2070           player->dynabombs_left > 0 ?
2071           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2072           EL_UNDEFINED);
2073 }
2074
2075 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2076 {
2077   // pos >= 0: get element from bottom of the stack;
2078   // pos <  0: get element from top of the stack
2079
2080   if (pos < 0)
2081   {
2082     int min_inventory_size = -pos;
2083     int inventory_pos = player->inventory_size - min_inventory_size;
2084     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2085
2086     return (player->inventory_size >= min_inventory_size ?
2087             player->inventory_element[inventory_pos] :
2088             player->inventory_infinite_element != EL_UNDEFINED ?
2089             player->inventory_infinite_element :
2090             player->dynabombs_left >= min_dynabombs_left ?
2091             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2092             EL_UNDEFINED);
2093   }
2094   else
2095   {
2096     int min_dynabombs_left = pos + 1;
2097     int min_inventory_size = pos + 1 - player->dynabombs_left;
2098     int inventory_pos = pos - player->dynabombs_left;
2099
2100     return (player->inventory_infinite_element != EL_UNDEFINED ?
2101             player->inventory_infinite_element :
2102             player->dynabombs_left >= min_dynabombs_left ?
2103             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2104             player->inventory_size >= min_inventory_size ?
2105             player->inventory_element[inventory_pos] :
2106             EL_UNDEFINED);
2107   }
2108 }
2109
2110 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2111 {
2112   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2113   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2114   int compare_result;
2115
2116   if (gpo1->sort_priority != gpo2->sort_priority)
2117     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2118   else
2119     compare_result = gpo1->nr - gpo2->nr;
2120
2121   return compare_result;
2122 }
2123
2124 int getPlayerInventorySize(int player_nr)
2125 {
2126   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2127     return level.native_em_level->ply[player_nr]->dynamite;
2128   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2129     return level.native_sp_level->game_sp->red_disk_count;
2130   else
2131     return stored_player[player_nr].inventory_size;
2132 }
2133
2134 static void InitGameControlValues(void)
2135 {
2136   int i;
2137
2138   for (i = 0; game_panel_controls[i].nr != -1; i++)
2139   {
2140     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2141     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2142     struct TextPosInfo *pos = gpc->pos;
2143     int nr = gpc->nr;
2144     int type = gpc->type;
2145
2146     if (nr != i)
2147     {
2148       Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2149       Error(ERR_EXIT, "this should not happen -- please debug");
2150     }
2151
2152     // force update of game controls after initialization
2153     gpc->value = gpc->last_value = -1;
2154     gpc->frame = gpc->last_frame = -1;
2155     gpc->gfx_frame = -1;
2156
2157     // determine panel value width for later calculation of alignment
2158     if (type == TYPE_INTEGER || type == TYPE_STRING)
2159     {
2160       pos->width = pos->size * getFontWidth(pos->font);
2161       pos->height = getFontHeight(pos->font);
2162     }
2163     else if (type == TYPE_ELEMENT)
2164     {
2165       pos->width = pos->size;
2166       pos->height = pos->size;
2167     }
2168
2169     // fill structure for game panel draw order
2170     gpo->nr = gpc->nr;
2171     gpo->sort_priority = pos->sort_priority;
2172   }
2173
2174   // sort game panel controls according to sort_priority and control number
2175   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2176         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2177 }
2178
2179 static void UpdatePlayfieldElementCount(void)
2180 {
2181   boolean use_element_count = FALSE;
2182   int i, j, x, y;
2183
2184   // first check if it is needed at all to calculate playfield element count
2185   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2186     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2187       use_element_count = TRUE;
2188
2189   if (!use_element_count)
2190     return;
2191
2192   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2193     element_info[i].element_count = 0;
2194
2195   SCAN_PLAYFIELD(x, y)
2196   {
2197     element_info[Feld[x][y]].element_count++;
2198   }
2199
2200   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2201     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2202       if (IS_IN_GROUP(j, i))
2203         element_info[EL_GROUP_START + i].element_count +=
2204           element_info[j].element_count;
2205 }
2206
2207 static void UpdateGameControlValues(void)
2208 {
2209   int i, k;
2210   int time = (local_player->LevelSolved ?
2211               local_player->LevelSolved_CountingTime :
2212               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2213               level.native_em_level->lev->time :
2214               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2215               level.native_sp_level->game_sp->time_played :
2216               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2217               game_mm.energy_left :
2218               game.no_time_limit ? TimePlayed : TimeLeft);
2219   int score = (local_player->LevelSolved ?
2220                local_player->LevelSolved_CountingScore :
2221                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2222                level.native_em_level->lev->score :
2223                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2224                level.native_sp_level->game_sp->score :
2225                level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2226                game_mm.score :
2227                local_player->score);
2228   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2229               level.native_em_level->lev->required :
2230               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2231               level.native_sp_level->game_sp->infotrons_still_needed :
2232               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2233               game_mm.kettles_still_needed :
2234               local_player->gems_still_needed);
2235   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2236                      level.native_em_level->lev->required > 0 :
2237                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2238                      level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2239                      level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2240                      game_mm.kettles_still_needed > 0 ||
2241                      game_mm.lights_still_needed > 0 :
2242                      local_player->gems_still_needed > 0 ||
2243                      local_player->sokoban_fields_still_needed > 0 ||
2244                      local_player->sokoban_objects_still_needed > 0 ||
2245                      local_player->lights_still_needed > 0);
2246   int health = (local_player->LevelSolved ?
2247                 local_player->LevelSolved_CountingHealth :
2248                 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2249                 MM_HEALTH(game_mm.laser_overload_value) :
2250                 local_player->health);
2251
2252   UpdatePlayfieldElementCount();
2253
2254   // update game panel control values
2255
2256   // used instead of "level_nr" (for network games)
2257   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = levelset.level_nr;
2258   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2259
2260   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2261   for (i = 0; i < MAX_NUM_KEYS; i++)
2262     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2263   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2264   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2265
2266   if (game.centered_player_nr == -1)
2267   {
2268     for (i = 0; i < MAX_PLAYERS; i++)
2269     {
2270       // only one player in Supaplex game engine
2271       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2272         break;
2273
2274       for (k = 0; k < MAX_NUM_KEYS; k++)
2275       {
2276         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2277         {
2278           if (level.native_em_level->ply[i]->keys & (1 << k))
2279             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2280               get_key_element_from_nr(k);
2281         }
2282         else if (stored_player[i].key[k])
2283           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2284             get_key_element_from_nr(k);
2285       }
2286
2287       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2288         getPlayerInventorySize(i);
2289
2290       if (stored_player[i].num_white_keys > 0)
2291         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2292           EL_DC_KEY_WHITE;
2293
2294       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2295         stored_player[i].num_white_keys;
2296     }
2297   }
2298   else
2299   {
2300     int player_nr = game.centered_player_nr;
2301
2302     for (k = 0; k < MAX_NUM_KEYS; k++)
2303     {
2304       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2305       {
2306         if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2307           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2308             get_key_element_from_nr(k);
2309       }
2310       else if (stored_player[player_nr].key[k])
2311         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2312           get_key_element_from_nr(k);
2313     }
2314
2315     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2316       getPlayerInventorySize(player_nr);
2317
2318     if (stored_player[player_nr].num_white_keys > 0)
2319       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2320
2321     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2322       stored_player[player_nr].num_white_keys;
2323   }
2324
2325   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2326   {
2327     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2328       get_inventory_element_from_pos(local_player, i);
2329     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2330       get_inventory_element_from_pos(local_player, -i - 1);
2331   }
2332
2333   game_panel_controls[GAME_PANEL_SCORE].value = score;
2334   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2335
2336   game_panel_controls[GAME_PANEL_TIME].value = time;
2337
2338   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2339   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2340   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2341
2342   if (level.time == 0)
2343     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2344   else
2345     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2346
2347   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2348   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2349
2350   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2351
2352   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2353     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2354      EL_EMPTY);
2355   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2356     local_player->shield_normal_time_left;
2357   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2358     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2359      EL_EMPTY);
2360   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2361     local_player->shield_deadly_time_left;
2362
2363   game_panel_controls[GAME_PANEL_EXIT].value =
2364     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2365
2366   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2367     (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2368   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2369     (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2370      EL_EMC_MAGIC_BALL_SWITCH);
2371
2372   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2373     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2374   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2375     game.light_time_left;
2376
2377   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2378     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2379   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2380     game.timegate_time_left;
2381
2382   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2383     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2384
2385   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2386     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2387   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2388     game.lenses_time_left;
2389
2390   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2391     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2392   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2393     game.magnify_time_left;
2394
2395   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2396     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2397      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2398      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2399      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2400      EL_BALLOON_SWITCH_NONE);
2401
2402   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2403     local_player->dynabomb_count;
2404   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2405     local_player->dynabomb_size;
2406   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2407     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2408
2409   game_panel_controls[GAME_PANEL_PENGUINS].value =
2410     local_player->friends_still_needed;
2411
2412   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2413     local_player->sokoban_objects_still_needed;
2414   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2415     local_player->sokoban_fields_still_needed;
2416
2417   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2418     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2419
2420   for (i = 0; i < NUM_BELTS; i++)
2421   {
2422     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2423       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2424        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2425     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2426       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2427   }
2428
2429   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2430     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2431   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2432     game.magic_wall_time_left;
2433
2434   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2435     local_player->gravity;
2436
2437   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2438     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2439
2440   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2441     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2442       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2443        game.panel.element[i].id : EL_UNDEFINED);
2444
2445   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2446     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2447       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2448        element_info[game.panel.element_count[i].id].element_count : 0);
2449
2450   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2451     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2452       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2453        element_info[game.panel.ce_score[i].id].collect_score : 0);
2454
2455   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2456     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2457       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2458        element_info[game.panel.ce_score_element[i].id].collect_score :
2459        EL_UNDEFINED);
2460
2461   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2462   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2463   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2464
2465   // update game panel control frames
2466
2467   for (i = 0; game_panel_controls[i].nr != -1; i++)
2468   {
2469     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2470
2471     if (gpc->type == TYPE_ELEMENT)
2472     {
2473       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2474       {
2475         int last_anim_random_frame = gfx.anim_random_frame;
2476         int element = gpc->value;
2477         int graphic = el2panelimg(element);
2478
2479         if (gpc->value != gpc->last_value)
2480         {
2481           gpc->gfx_frame = 0;
2482           gpc->gfx_random = INIT_GFX_RANDOM();
2483         }
2484         else
2485         {
2486           gpc->gfx_frame++;
2487
2488           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2489               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2490             gpc->gfx_random = INIT_GFX_RANDOM();
2491         }
2492
2493         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2494           gfx.anim_random_frame = gpc->gfx_random;
2495
2496         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2497           gpc->gfx_frame = element_info[element].collect_score;
2498
2499         gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2500                                               gpc->gfx_frame);
2501
2502         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2503           gfx.anim_random_frame = last_anim_random_frame;
2504       }
2505     }
2506     else if (gpc->type == TYPE_GRAPHIC)
2507     {
2508       if (gpc->graphic != IMG_UNDEFINED)
2509       {
2510         int last_anim_random_frame = gfx.anim_random_frame;
2511         int graphic = gpc->graphic;
2512
2513         if (gpc->value != gpc->last_value)
2514         {
2515           gpc->gfx_frame = 0;
2516           gpc->gfx_random = INIT_GFX_RANDOM();
2517         }
2518         else
2519         {
2520           gpc->gfx_frame++;
2521
2522           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2523               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2524             gpc->gfx_random = INIT_GFX_RANDOM();
2525         }
2526
2527         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2528           gfx.anim_random_frame = gpc->gfx_random;
2529
2530         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2531
2532         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2533           gfx.anim_random_frame = last_anim_random_frame;
2534       }
2535     }
2536   }
2537 }
2538
2539 static void DisplayGameControlValues(void)
2540 {
2541   boolean redraw_panel = FALSE;
2542   int i;
2543
2544   for (i = 0; game_panel_controls[i].nr != -1; i++)
2545   {
2546     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2547
2548     if (PANEL_DEACTIVATED(gpc->pos))
2549       continue;
2550
2551     if (gpc->value == gpc->last_value &&
2552         gpc->frame == gpc->last_frame)
2553       continue;
2554
2555     redraw_panel = TRUE;
2556   }
2557
2558   if (!redraw_panel)
2559     return;
2560
2561   // copy default game door content to main double buffer
2562
2563   // !!! CHECK AGAIN !!!
2564   SetPanelBackground();
2565   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2566   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2567
2568   // redraw game control buttons
2569   RedrawGameButtons();
2570
2571   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2572
2573   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2574   {
2575     int nr = game_panel_order[i].nr;
2576     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2577     struct TextPosInfo *pos = gpc->pos;
2578     int type = gpc->type;
2579     int value = gpc->value;
2580     int frame = gpc->frame;
2581     int size = pos->size;
2582     int font = pos->font;
2583     boolean draw_masked = pos->draw_masked;
2584     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2585
2586     if (PANEL_DEACTIVATED(pos))
2587       continue;
2588
2589     gpc->last_value = value;
2590     gpc->last_frame = frame;
2591
2592     if (type == TYPE_INTEGER)
2593     {
2594       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2595           nr == GAME_PANEL_TIME)
2596       {
2597         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2598
2599         if (use_dynamic_size)           // use dynamic number of digits
2600         {
2601           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2602           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2603           int size2 = size1 + 1;
2604           int font1 = pos->font;
2605           int font2 = pos->font_alt;
2606
2607           size = (value < value_change ? size1 : size2);
2608           font = (value < value_change ? font1 : font2);
2609         }
2610       }
2611
2612       // correct text size if "digits" is zero or less
2613       if (size <= 0)
2614         size = strlen(int2str(value, size));
2615
2616       // dynamically correct text alignment
2617       pos->width = size * getFontWidth(font);
2618
2619       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2620                   int2str(value, size), font, mask_mode);
2621     }
2622     else if (type == TYPE_ELEMENT)
2623     {
2624       int element, graphic;
2625       Bitmap *src_bitmap;
2626       int src_x, src_y;
2627       int width, height;
2628       int dst_x = PANEL_XPOS(pos);
2629       int dst_y = PANEL_YPOS(pos);
2630
2631       if (value != EL_UNDEFINED && value != EL_EMPTY)
2632       {
2633         element = value;
2634         graphic = el2panelimg(value);
2635
2636         // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2637
2638         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2639           size = TILESIZE;
2640
2641         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2642                               &src_x, &src_y);
2643
2644         width  = graphic_info[graphic].width  * size / TILESIZE;
2645         height = graphic_info[graphic].height * size / TILESIZE;
2646
2647         if (draw_masked)
2648           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2649                            dst_x, dst_y);
2650         else
2651           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2652                      dst_x, dst_y);
2653       }
2654     }
2655     else if (type == TYPE_GRAPHIC)
2656     {
2657       int graphic        = gpc->graphic;
2658       int graphic_active = gpc->graphic_active;
2659       Bitmap *src_bitmap;
2660       int src_x, src_y;
2661       int width, height;
2662       int dst_x = PANEL_XPOS(pos);
2663       int dst_y = PANEL_YPOS(pos);
2664       boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2665                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2666
2667       if (graphic != IMG_UNDEFINED && !skip)
2668       {
2669         if (pos->style == STYLE_REVERSE)
2670           value = 100 - value;
2671
2672         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2673
2674         if (pos->direction & MV_HORIZONTAL)
2675         {
2676           width  = graphic_info[graphic_active].width * value / 100;
2677           height = graphic_info[graphic_active].height;
2678
2679           if (pos->direction == MV_LEFT)
2680           {
2681             src_x += graphic_info[graphic_active].width - width;
2682             dst_x += graphic_info[graphic_active].width - width;
2683           }
2684         }
2685         else
2686         {
2687           width  = graphic_info[graphic_active].width;
2688           height = graphic_info[graphic_active].height * value / 100;
2689
2690           if (pos->direction == MV_UP)
2691           {
2692             src_y += graphic_info[graphic_active].height - height;
2693             dst_y += graphic_info[graphic_active].height - height;
2694           }
2695         }
2696
2697         if (draw_masked)
2698           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2699                            dst_x, dst_y);
2700         else
2701           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2702                      dst_x, dst_y);
2703
2704         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2705
2706         if (pos->direction & MV_HORIZONTAL)
2707         {
2708           if (pos->direction == MV_RIGHT)
2709           {
2710             src_x += width;
2711             dst_x += width;
2712           }
2713           else
2714           {
2715             dst_x = PANEL_XPOS(pos);
2716           }
2717
2718           width = graphic_info[graphic].width - width;
2719         }
2720         else
2721         {
2722           if (pos->direction == MV_DOWN)
2723           {
2724             src_y += height;
2725             dst_y += height;
2726           }
2727           else
2728           {
2729             dst_y = PANEL_YPOS(pos);
2730           }
2731
2732           height = graphic_info[graphic].height - height;
2733         }
2734
2735         if (draw_masked)
2736           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2737                            dst_x, dst_y);
2738         else
2739           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2740                      dst_x, dst_y);
2741       }
2742     }
2743     else if (type == TYPE_STRING)
2744     {
2745       boolean active = (value != 0);
2746       char *state_normal = "off";
2747       char *state_active = "on";
2748       char *state = (active ? state_active : state_normal);
2749       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2750                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2751                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2752                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2753
2754       if (nr == GAME_PANEL_GRAVITY_STATE)
2755       {
2756         int font1 = pos->font;          // (used for normal state)
2757         int font2 = pos->font_alt;      // (used for active state)
2758
2759         font = (active ? font2 : font1);
2760       }
2761
2762       if (s != NULL)
2763       {
2764         char *s_cut;
2765
2766         if (size <= 0)
2767         {
2768           // don't truncate output if "chars" is zero or less
2769           size = strlen(s);
2770
2771           // dynamically correct text alignment
2772           pos->width = size * getFontWidth(font);
2773         }
2774
2775         s_cut = getStringCopyN(s, size);
2776
2777         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2778                     s_cut, font, mask_mode);
2779
2780         free(s_cut);
2781       }
2782     }
2783
2784     redraw_mask |= REDRAW_DOOR_1;
2785   }
2786
2787   SetGameStatus(GAME_MODE_PLAYING);
2788 }
2789
2790 void UpdateAndDisplayGameControlValues(void)
2791 {
2792   if (tape.deactivate_display)
2793     return;
2794
2795   UpdateGameControlValues();
2796   DisplayGameControlValues();
2797 }
2798
2799 #if 0
2800 static void UpdateGameDoorValues(void)
2801 {
2802   UpdateGameControlValues();
2803 }
2804 #endif
2805
2806 void DrawGameDoorValues(void)
2807 {
2808   DisplayGameControlValues();
2809 }
2810
2811
2812 // ============================================================================
2813 // InitGameEngine()
2814 // ----------------------------------------------------------------------------
2815 // initialize game engine due to level / tape version number
2816 // ============================================================================
2817
2818 static void InitGameEngine(void)
2819 {
2820   int i, j, k, l, x, y;
2821
2822   // set game engine from tape file when re-playing, else from level file
2823   game.engine_version = (tape.playing ? tape.engine_version :
2824                          level.game_version);
2825
2826   // set single or multi-player game mode (needed for re-playing tapes)
2827   game.team_mode = setup.team_mode;
2828
2829   if (tape.playing)
2830   {
2831     int num_players = 0;
2832
2833     for (i = 0; i < MAX_PLAYERS; i++)
2834       if (tape.player_participates[i])
2835         num_players++;
2836
2837     // multi-player tapes contain input data for more than one player
2838     game.team_mode = (num_players > 1);
2839   }
2840
2841   // --------------------------------------------------------------------------
2842   // set flags for bugs and changes according to active game engine version
2843   // --------------------------------------------------------------------------
2844
2845   /*
2846     Summary of bugfix/change:
2847     Fixed handling for custom elements that change when pushed by the player.
2848
2849     Fixed/changed in version:
2850     3.1.0
2851
2852     Description:
2853     Before 3.1.0, custom elements that "change when pushing" changed directly
2854     after the player started pushing them (until then handled in "DigField()").
2855     Since 3.1.0, these custom elements are not changed until the "pushing"
2856     move of the element is finished (now handled in "ContinueMoving()").
2857
2858     Affected levels/tapes:
2859     The first condition is generally needed for all levels/tapes before version
2860     3.1.0, which might use the old behaviour before it was changed; known tapes
2861     that are affected are some tapes from the level set "Walpurgis Gardens" by
2862     Jamie Cullen.
2863     The second condition is an exception from the above case and is needed for
2864     the special case of tapes recorded with game (not engine!) version 3.1.0 or
2865     above (including some development versions of 3.1.0), but before it was
2866     known that this change would break tapes like the above and was fixed in
2867     3.1.1, so that the changed behaviour was active although the engine version
2868     while recording maybe was before 3.1.0. There is at least one tape that is
2869     affected by this exception, which is the tape for the one-level set "Bug
2870     Machine" by Juergen Bonhagen.
2871   */
2872
2873   game.use_change_when_pushing_bug =
2874     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2875      !(tape.playing &&
2876        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2877        tape.game_version <  VERSION_IDENT(3,1,1,0)));
2878
2879   /*
2880     Summary of bugfix/change:
2881     Fixed handling for blocking the field the player leaves when moving.
2882
2883     Fixed/changed in version:
2884     3.1.1
2885
2886     Description:
2887     Before 3.1.1, when "block last field when moving" was enabled, the field
2888     the player is leaving when moving was blocked for the time of the move,
2889     and was directly unblocked afterwards. This resulted in the last field
2890     being blocked for exactly one less than the number of frames of one player
2891     move. Additionally, even when blocking was disabled, the last field was
2892     blocked for exactly one frame.
2893     Since 3.1.1, due to changes in player movement handling, the last field
2894     is not blocked at all when blocking is disabled. When blocking is enabled,
2895     the last field is blocked for exactly the number of frames of one player
2896     move. Additionally, if the player is Murphy, the hero of Supaplex, the
2897     last field is blocked for exactly one more than the number of frames of
2898     one player move.
2899
2900     Affected levels/tapes:
2901     (!!! yet to be determined -- probably many !!!)
2902   */
2903
2904   game.use_block_last_field_bug =
2905     (game.engine_version < VERSION_IDENT(3,1,1,0));
2906
2907   game_em.use_single_button =
2908     (game.engine_version > VERSION_IDENT(4,0,0,2));
2909
2910   game_em.use_snap_key_bug =
2911     (game.engine_version < VERSION_IDENT(4,0,1,0));
2912
2913   // --------------------------------------------------------------------------
2914
2915   // set maximal allowed number of custom element changes per game frame
2916   game.max_num_changes_per_frame = 1;
2917
2918   // default scan direction: scan playfield from top/left to bottom/right
2919   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2920
2921   // dynamically adjust element properties according to game engine version
2922   InitElementPropertiesEngine(game.engine_version);
2923
2924 #if 0
2925   printf("level %d: level version == %06d\n", level_nr, level.game_version);
2926   printf("          tape version == %06d [%s] [file: %06d]\n",
2927          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2928          tape.file_version);
2929   printf("       => game.engine_version == %06d\n", game.engine_version);
2930 #endif
2931
2932   // ---------- initialize player's initial move delay ------------------------
2933
2934   // dynamically adjust player properties according to level information
2935   for (i = 0; i < MAX_PLAYERS; i++)
2936     game.initial_move_delay_value[i] =
2937       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2938
2939   // dynamically adjust player properties according to game engine version
2940   for (i = 0; i < MAX_PLAYERS; i++)
2941     game.initial_move_delay[i] =
2942       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2943        game.initial_move_delay_value[i] : 0);
2944
2945   // ---------- initialize player's initial push delay ------------------------
2946
2947   // dynamically adjust player properties according to game engine version
2948   game.initial_push_delay_value =
2949     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2950
2951   // ---------- initialize changing elements ----------------------------------
2952
2953   // initialize changing elements information
2954   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2955   {
2956     struct ElementInfo *ei = &element_info[i];
2957
2958     // this pointer might have been changed in the level editor
2959     ei->change = &ei->change_page[0];
2960
2961     if (!IS_CUSTOM_ELEMENT(i))
2962     {
2963       ei->change->target_element = EL_EMPTY_SPACE;
2964       ei->change->delay_fixed = 0;
2965       ei->change->delay_random = 0;
2966       ei->change->delay_frames = 1;
2967     }
2968
2969     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2970     {
2971       ei->has_change_event[j] = FALSE;
2972
2973       ei->event_page_nr[j] = 0;
2974       ei->event_page[j] = &ei->change_page[0];
2975     }
2976   }
2977
2978   // add changing elements from pre-defined list
2979   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2980   {
2981     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2982     struct ElementInfo *ei = &element_info[ch_delay->element];
2983
2984     ei->change->target_element       = ch_delay->target_element;
2985     ei->change->delay_fixed          = ch_delay->change_delay;
2986
2987     ei->change->pre_change_function  = ch_delay->pre_change_function;
2988     ei->change->change_function      = ch_delay->change_function;
2989     ei->change->post_change_function = ch_delay->post_change_function;
2990
2991     ei->change->can_change = TRUE;
2992     ei->change->can_change_or_has_action = TRUE;
2993
2994     ei->has_change_event[CE_DELAY] = TRUE;
2995
2996     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
2997     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
2998   }
2999
3000   // ---------- initialize internal run-time variables ------------------------
3001
3002   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3003   {
3004     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3005
3006     for (j = 0; j < ei->num_change_pages; j++)
3007     {
3008       ei->change_page[j].can_change_or_has_action =
3009         (ei->change_page[j].can_change |
3010          ei->change_page[j].has_action);
3011     }
3012   }
3013
3014   // add change events from custom element configuration
3015   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3016   {
3017     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3018
3019     for (j = 0; j < ei->num_change_pages; j++)
3020     {
3021       if (!ei->change_page[j].can_change_or_has_action)
3022         continue;
3023
3024       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3025       {
3026         // only add event page for the first page found with this event
3027         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3028         {
3029           ei->has_change_event[k] = TRUE;
3030
3031           ei->event_page_nr[k] = j;
3032           ei->event_page[k] = &ei->change_page[j];
3033         }
3034       }
3035     }
3036   }
3037
3038   // ---------- initialize reference elements in change conditions ------------
3039
3040   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3041   {
3042     int element = EL_CUSTOM_START + i;
3043     struct ElementInfo *ei = &element_info[element];
3044
3045     for (j = 0; j < ei->num_change_pages; j++)
3046     {
3047       int trigger_element = ei->change_page[j].initial_trigger_element;
3048
3049       if (trigger_element >= EL_PREV_CE_8 &&
3050           trigger_element <= EL_NEXT_CE_8)
3051         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3052
3053       ei->change_page[j].trigger_element = trigger_element;
3054     }
3055   }
3056
3057   // ---------- initialize run-time trigger player and element ----------------
3058
3059   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3060   {
3061     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3062
3063     for (j = 0; j < ei->num_change_pages; j++)
3064     {
3065       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3066       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3067       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3068       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3069       ei->change_page[j].actual_trigger_ce_value = 0;
3070       ei->change_page[j].actual_trigger_ce_score = 0;
3071     }
3072   }
3073
3074   // ---------- initialize trigger events -------------------------------------
3075
3076   // initialize trigger events information
3077   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3078     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3079       trigger_events[i][j] = FALSE;
3080
3081   // add trigger events from element change event properties
3082   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3083   {
3084     struct ElementInfo *ei = &element_info[i];
3085
3086     for (j = 0; j < ei->num_change_pages; j++)
3087     {
3088       if (!ei->change_page[j].can_change_or_has_action)
3089         continue;
3090
3091       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3092       {
3093         int trigger_element = ei->change_page[j].trigger_element;
3094
3095         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3096         {
3097           if (ei->change_page[j].has_event[k])
3098           {
3099             if (IS_GROUP_ELEMENT(trigger_element))
3100             {
3101               struct ElementGroupInfo *group =
3102                 element_info[trigger_element].group;
3103
3104               for (l = 0; l < group->num_elements_resolved; l++)
3105                 trigger_events[group->element_resolved[l]][k] = TRUE;
3106             }
3107             else if (trigger_element == EL_ANY_ELEMENT)
3108               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3109                 trigger_events[l][k] = TRUE;
3110             else
3111               trigger_events[trigger_element][k] = TRUE;
3112           }
3113         }
3114       }
3115     }
3116   }
3117
3118   // ---------- initialize push delay -----------------------------------------
3119
3120   // initialize push delay values to default
3121   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3122   {
3123     if (!IS_CUSTOM_ELEMENT(i))
3124     {
3125       // set default push delay values (corrected since version 3.0.7-1)
3126       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3127       {
3128         element_info[i].push_delay_fixed = 2;
3129         element_info[i].push_delay_random = 8;
3130       }
3131       else
3132       {
3133         element_info[i].push_delay_fixed = 8;
3134         element_info[i].push_delay_random = 8;
3135       }
3136     }
3137   }
3138
3139   // set push delay value for certain elements from pre-defined list
3140   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3141   {
3142     int e = push_delay_list[i].element;
3143
3144     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3145     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3146   }
3147
3148   // set push delay value for Supaplex elements for newer engine versions
3149   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3150   {
3151     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3152     {
3153       if (IS_SP_ELEMENT(i))
3154       {
3155         // set SP push delay to just enough to push under a falling zonk
3156         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3157
3158         element_info[i].push_delay_fixed  = delay;
3159         element_info[i].push_delay_random = 0;
3160       }
3161     }
3162   }
3163
3164   // ---------- initialize move stepsize --------------------------------------
3165
3166   // initialize move stepsize values to default
3167   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3168     if (!IS_CUSTOM_ELEMENT(i))
3169       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3170
3171   // set move stepsize value for certain elements from pre-defined list
3172   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3173   {
3174     int e = move_stepsize_list[i].element;
3175
3176     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3177   }
3178
3179   // ---------- initialize collect score --------------------------------------
3180
3181   // initialize collect score values for custom elements from initial value
3182   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3183     if (IS_CUSTOM_ELEMENT(i))
3184       element_info[i].collect_score = element_info[i].collect_score_initial;
3185
3186   // ---------- initialize collect count --------------------------------------
3187
3188   // initialize collect count values for non-custom elements
3189   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3190     if (!IS_CUSTOM_ELEMENT(i))
3191       element_info[i].collect_count_initial = 0;
3192
3193   // add collect count values for all elements from pre-defined list
3194   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3195     element_info[collect_count_list[i].element].collect_count_initial =
3196       collect_count_list[i].count;
3197
3198   // ---------- initialize access direction -----------------------------------
3199
3200   // initialize access direction values to default (access from every side)
3201   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3202     if (!IS_CUSTOM_ELEMENT(i))
3203       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3204
3205   // set access direction value for certain elements from pre-defined list
3206   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3207     element_info[access_direction_list[i].element].access_direction =
3208       access_direction_list[i].direction;
3209
3210   // ---------- initialize explosion content ----------------------------------
3211   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3212   {
3213     if (IS_CUSTOM_ELEMENT(i))
3214       continue;
3215
3216     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3217     {
3218       // (content for EL_YAMYAM set at run-time with game.yamyam_content_nr)
3219
3220       element_info[i].content.e[x][y] =
3221         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3222          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3223          i == EL_PLAYER_3 ? EL_EMERALD :
3224          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3225          i == EL_MOLE ? EL_EMERALD_RED :
3226          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3227          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3228          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3229          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3230          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3231          i == EL_WALL_EMERALD ? EL_EMERALD :
3232          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3233          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3234          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3235          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3236          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3237          i == EL_WALL_PEARL ? EL_PEARL :
3238          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3239          EL_EMPTY);
3240     }
3241   }
3242
3243   // ---------- initialize recursion detection --------------------------------
3244   recursion_loop_depth = 0;
3245   recursion_loop_detected = FALSE;
3246   recursion_loop_element = EL_UNDEFINED;
3247
3248   // ---------- initialize graphics engine ------------------------------------
3249   game.scroll_delay_value =
3250     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3251      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3252   game.scroll_delay_value =
3253     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3254
3255   // ---------- initialize game engine snapshots ------------------------------
3256   for (i = 0; i < MAX_PLAYERS; i++)
3257     game.snapshot.last_action[i] = 0;
3258   game.snapshot.changed_action = FALSE;
3259   game.snapshot.collected_item = FALSE;
3260   game.snapshot.mode =
3261     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3262      SNAPSHOT_MODE_EVERY_STEP :
3263      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3264      SNAPSHOT_MODE_EVERY_MOVE :
3265      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3266      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3267   game.snapshot.save_snapshot = FALSE;
3268
3269   // ---------- initialize level time for Supaplex engine ---------------------
3270   // Supaplex levels with time limit currently unsupported -- should be added
3271   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3272     level.time = 0;
3273 }
3274
3275 static int get_num_special_action(int element, int action_first,
3276                                   int action_last)
3277 {
3278   int num_special_action = 0;
3279   int i, j;
3280
3281   for (i = action_first; i <= action_last; i++)
3282   {
3283     boolean found = FALSE;
3284
3285     for (j = 0; j < NUM_DIRECTIONS; j++)
3286       if (el_act_dir2img(element, i, j) !=
3287           el_act_dir2img(element, ACTION_DEFAULT, j))
3288         found = TRUE;
3289
3290     if (found)
3291       num_special_action++;
3292     else
3293       break;
3294   }
3295
3296   return num_special_action;
3297 }
3298
3299
3300 // ============================================================================
3301 // InitGame()
3302 // ----------------------------------------------------------------------------
3303 // initialize and start new game
3304 // ============================================================================
3305
3306 #if DEBUG_INIT_PLAYER
3307 static void DebugPrintPlayerStatus(char *message)
3308 {
3309   int i;
3310
3311   if (!options.debug)
3312     return;
3313
3314   printf("%s:\n", message);
3315
3316   for (i = 0; i < MAX_PLAYERS; i++)
3317   {
3318     struct PlayerInfo *player = &stored_player[i];
3319
3320     printf("- player %d: present == %d, connected == %d [%d/%d], active == %d",
3321            i + 1,
3322            player->present,
3323            player->connected,
3324            player->connected_locally,
3325            player->connected_network,
3326            player->active);
3327
3328     if (local_player == player)
3329       printf(" (local player)");
3330
3331     printf("\n");
3332   }
3333 }
3334 #endif
3335
3336 void InitGame(void)
3337 {
3338   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3339   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3340   int fade_mask = REDRAW_FIELD;
3341
3342   boolean emulate_bd = TRUE;    // unless non-BOULDERDASH elements found
3343   boolean emulate_sb = TRUE;    // unless non-SOKOBAN     elements found
3344   boolean emulate_sp = TRUE;    // unless non-SUPAPLEX    elements found
3345   int initial_move_dir = MV_DOWN;
3346   int i, j, x, y;
3347
3348   // required here to update video display before fading (FIX THIS)
3349   DrawMaskedBorder(REDRAW_DOOR_2);
3350
3351   if (!game.restart_level)
3352     CloseDoor(DOOR_CLOSE_1);
3353
3354   SetGameStatus(GAME_MODE_PLAYING);
3355
3356   if (level_editor_test_game)
3357     FadeSkipNextFadeIn();
3358   else
3359     FadeSetEnterScreen();
3360
3361   if (CheckIfGlobalBorderOrPlayfieldViewportHasChanged())
3362     fade_mask = REDRAW_ALL;
3363
3364   FadeLevelSoundsAndMusic();
3365
3366   ExpireSoundLoops(TRUE);
3367
3368   FadeOut(fade_mask);
3369
3370   // needed if different viewport properties defined for playing
3371   ChangeViewportPropertiesIfNeeded();
3372
3373   ClearField();
3374
3375   DrawCompleteVideoDisplay();
3376
3377   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3378
3379   InitGameEngine();
3380   InitGameControlValues();
3381
3382   // don't play tapes over network
3383   network_playing = (network.enabled && !tape.playing);
3384
3385   for (i = 0; i < MAX_PLAYERS; i++)
3386   {
3387     struct PlayerInfo *player = &stored_player[i];
3388
3389     player->index_nr = i;
3390     player->index_bit = (1 << i);
3391     player->element_nr = EL_PLAYER_1 + i;
3392
3393     player->present = FALSE;
3394     player->active = FALSE;
3395     player->mapped = FALSE;
3396
3397     player->killed = FALSE;
3398     player->reanimated = FALSE;
3399
3400     player->action = 0;
3401     player->effective_action = 0;
3402     player->programmed_action = 0;
3403
3404     player->mouse_action.lx = 0;
3405     player->mouse_action.ly = 0;
3406     player->mouse_action.button = 0;
3407     player->mouse_action.button_hint = 0;
3408
3409     player->effective_mouse_action.lx = 0;
3410     player->effective_mouse_action.ly = 0;
3411     player->effective_mouse_action.button = 0;
3412     player->effective_mouse_action.button_hint = 0;
3413
3414     player->score = 0;
3415     player->score_final = 0;
3416
3417     player->health = MAX_HEALTH;
3418     player->health_final = MAX_HEALTH;
3419
3420     player->gems_still_needed = level.gems_needed;
3421     player->sokoban_fields_still_needed = 0;
3422     player->sokoban_objects_still_needed = 0;
3423     player->lights_still_needed = 0;
3424     player->players_still_needed = 0;
3425     player->friends_still_needed = 0;
3426
3427     for (j = 0; j < MAX_NUM_KEYS; j++)
3428       player->key[j] = FALSE;
3429
3430     player->num_white_keys = 0;
3431
3432     player->dynabomb_count = 0;
3433     player->dynabomb_size = 1;
3434     player->dynabombs_left = 0;
3435     player->dynabomb_xl = FALSE;
3436
3437     player->MovDir = initial_move_dir;
3438     player->MovPos = 0;
3439     player->GfxPos = 0;
3440     player->GfxDir = initial_move_dir;
3441     player->GfxAction = ACTION_DEFAULT;
3442     player->Frame = 0;
3443     player->StepFrame = 0;
3444
3445     player->initial_element = player->element_nr;
3446     player->artwork_element =
3447       (level.use_artwork_element[i] ? level.artwork_element[i] :
3448        player->element_nr);
3449     player->use_murphy = FALSE;
3450
3451     player->block_last_field = FALSE;   // initialized in InitPlayerField()
3452     player->block_delay_adjustment = 0; // initialized in InitPlayerField()
3453
3454     player->gravity = level.initial_player_gravity[i];
3455
3456     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3457
3458     player->actual_frame_counter = 0;
3459
3460     player->step_counter = 0;
3461
3462     player->last_move_dir = initial_move_dir;
3463
3464     player->is_active = FALSE;
3465
3466     player->is_waiting = FALSE;
3467     player->is_moving = FALSE;
3468     player->is_auto_moving = FALSE;
3469     player->is_digging = FALSE;
3470     player->is_snapping = FALSE;
3471     player->is_collecting = FALSE;
3472     player->is_pushing = FALSE;
3473     player->is_switching = FALSE;
3474     player->is_dropping = FALSE;
3475     player->is_dropping_pressed = FALSE;
3476
3477     player->is_bored = FALSE;
3478     player->is_sleeping = FALSE;
3479
3480     player->was_waiting = TRUE;
3481     player->was_moving = FALSE;
3482     player->was_snapping = FALSE;
3483     player->was_dropping = FALSE;
3484
3485     player->force_dropping = FALSE;
3486
3487     player->frame_counter_bored = -1;
3488     player->frame_counter_sleeping = -1;
3489
3490     player->anim_delay_counter = 0;
3491     player->post_delay_counter = 0;
3492
3493     player->dir_waiting = initial_move_dir;
3494     player->action_waiting = ACTION_DEFAULT;
3495     player->last_action_waiting = ACTION_DEFAULT;
3496     player->special_action_bored = ACTION_DEFAULT;
3497     player->special_action_sleeping = ACTION_DEFAULT;
3498
3499     player->switch_x = -1;
3500     player->switch_y = -1;
3501
3502     player->drop_x = -1;
3503     player->drop_y = -1;
3504
3505     player->show_envelope = 0;
3506
3507     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3508
3509     player->push_delay       = -1;      // initialized when pushing starts
3510     player->push_delay_value = game.initial_push_delay_value;
3511
3512     player->drop_delay = 0;
3513     player->drop_pressed_delay = 0;
3514
3515     player->last_jx = -1;
3516     player->last_jy = -1;
3517     player->jx = -1;
3518     player->jy = -1;
3519
3520     player->shield_normal_time_left = 0;
3521     player->shield_deadly_time_left = 0;
3522
3523     player->inventory_infinite_element = EL_UNDEFINED;
3524     player->inventory_size = 0;
3525
3526     if (level.use_initial_inventory[i])
3527     {
3528       for (j = 0; j < level.initial_inventory_size[i]; j++)
3529       {
3530         int element = level.initial_inventory_content[i][j];
3531         int collect_count = element_info[element].collect_count_initial;
3532         int k;
3533
3534         if (!IS_CUSTOM_ELEMENT(element))
3535           collect_count = 1;
3536
3537         if (collect_count == 0)
3538           player->inventory_infinite_element = element;
3539         else
3540           for (k = 0; k < collect_count; k++)
3541             if (player->inventory_size < MAX_INVENTORY_SIZE)
3542               player->inventory_element[player->inventory_size++] = element;
3543       }
3544     }
3545
3546     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3547     SnapField(player, 0, 0);
3548
3549     player->LevelSolved = FALSE;
3550     player->GameOver = FALSE;
3551
3552     player->LevelSolved_GameWon = FALSE;
3553     player->LevelSolved_GameEnd = FALSE;
3554     player->LevelSolved_SaveTape = FALSE;
3555     player->LevelSolved_SaveScore = FALSE;
3556
3557     player->LevelSolved_CountingTime = 0;
3558     player->LevelSolved_CountingScore = 0;
3559     player->LevelSolved_CountingHealth = 0;
3560
3561     map_player_action[i] = i;
3562   }
3563
3564   network_player_action_received = FALSE;
3565
3566   // initial null action
3567   if (network_playing)
3568     SendToServer_MovePlayer(MV_NONE);
3569
3570   ZX = ZY = -1;
3571   ExitX = ExitY = -1;
3572
3573   FrameCounter = 0;
3574   TimeFrames = 0;
3575   TimePlayed = 0;
3576   TimeLeft = level.time;
3577   TapeTime = 0;
3578
3579   ScreenMovDir = MV_NONE;
3580   ScreenMovPos = 0;
3581   ScreenGfxPos = 0;
3582
3583   ScrollStepSize = 0;   // will be correctly initialized by ScrollScreen()
3584
3585   AllPlayersGone = FALSE;
3586
3587   game.panel.active = TRUE;
3588
3589   game.no_time_limit = (level.time == 0);
3590
3591   game.yamyam_content_nr = 0;
3592   game.robot_wheel_active = FALSE;
3593   game.magic_wall_active = FALSE;
3594   game.magic_wall_time_left = 0;
3595   game.light_time_left = 0;
3596   game.timegate_time_left = 0;
3597   game.switchgate_pos = 0;
3598   game.wind_direction = level.wind_direction_initial;
3599
3600   game.lenses_time_left = 0;
3601   game.magnify_time_left = 0;
3602
3603   game.ball_state = level.ball_state_initial;
3604   game.ball_content_nr = 0;
3605
3606   game.explosions_delayed = TRUE;
3607
3608   game.envelope_active = FALSE;
3609
3610   for (i = 0; i < NUM_BELTS; i++)
3611   {
3612     game.belt_dir[i] = MV_NONE;
3613     game.belt_dir_nr[i] = 3;            // not moving, next moving left
3614   }
3615
3616   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3617     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3618
3619 #if DEBUG_INIT_PLAYER
3620   DebugPrintPlayerStatus("Player status at level initialization");
3621 #endif
3622
3623   SCAN_PLAYFIELD(x, y)
3624   {
3625     Feld[x][y] = Last[x][y] = level.field[x][y];
3626     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3627     ChangeDelay[x][y] = 0;
3628     ChangePage[x][y] = -1;
3629     CustomValue[x][y] = 0;              // initialized in InitField()
3630     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3631     AmoebaNr[x][y] = 0;
3632     WasJustMoving[x][y] = 0;
3633     WasJustFalling[x][y] = 0;
3634     CheckCollision[x][y] = 0;
3635     CheckImpact[x][y] = 0;
3636     Stop[x][y] = FALSE;
3637     Pushed[x][y] = FALSE;
3638
3639     ChangeCount[x][y] = 0;
3640     ChangeEvent[x][y] = -1;
3641
3642     ExplodePhase[x][y] = 0;
3643     ExplodeDelay[x][y] = 0;
3644     ExplodeField[x][y] = EX_TYPE_NONE;
3645
3646     RunnerVisit[x][y] = 0;
3647     PlayerVisit[x][y] = 0;
3648
3649     GfxFrame[x][y] = 0;
3650     GfxRandom[x][y] = INIT_GFX_RANDOM();
3651     GfxElement[x][y] = EL_UNDEFINED;
3652     GfxAction[x][y] = ACTION_DEFAULT;
3653     GfxDir[x][y] = MV_NONE;
3654     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3655   }
3656
3657   SCAN_PLAYFIELD(x, y)
3658   {
3659     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3660       emulate_bd = FALSE;
3661     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3662       emulate_sb = FALSE;
3663     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3664       emulate_sp = FALSE;
3665
3666     InitField(x, y, TRUE);
3667
3668     ResetGfxAnimation(x, y);
3669   }
3670
3671   InitBeltMovement();
3672
3673   for (i = 0; i < MAX_PLAYERS; i++)
3674   {
3675     struct PlayerInfo *player = &stored_player[i];
3676
3677     // set number of special actions for bored and sleeping animation
3678     player->num_special_action_bored =
3679       get_num_special_action(player->artwork_element,
3680                              ACTION_BORING_1, ACTION_BORING_LAST);
3681     player->num_special_action_sleeping =
3682       get_num_special_action(player->artwork_element,
3683                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3684   }
3685
3686   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3687                     emulate_sb ? EMU_SOKOBAN :
3688                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3689
3690   // initialize type of slippery elements
3691   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3692   {
3693     if (!IS_CUSTOM_ELEMENT(i))
3694     {
3695       // default: elements slip down either to the left or right randomly
3696       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3697
3698       // SP style elements prefer to slip down on the left side
3699       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3700         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3701
3702       // BD style elements prefer to slip down on the left side
3703       if (game.emulation == EMU_BOULDERDASH)
3704         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3705     }
3706   }
3707
3708   // initialize explosion and ignition delay
3709   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3710   {
3711     if (!IS_CUSTOM_ELEMENT(i))
3712     {
3713       int num_phase = 8;
3714       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3715                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3716                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3717       int last_phase = (num_phase + 1) * delay;
3718       int half_phase = (num_phase / 2) * delay;
3719
3720       element_info[i].explosion_delay = last_phase - 1;
3721       element_info[i].ignition_delay = half_phase;
3722
3723       if (i == EL_BLACK_ORB)
3724         element_info[i].ignition_delay = 1;
3725     }
3726   }
3727
3728   // correct non-moving belts to start moving left
3729   for (i = 0; i < NUM_BELTS; i++)
3730     if (game.belt_dir[i] == MV_NONE)
3731       game.belt_dir_nr[i] = 3;          // not moving, next moving left
3732
3733 #if USE_NEW_PLAYER_ASSIGNMENTS
3734   for (i = 0; i < MAX_PLAYERS; i++)
3735   {
3736     stored_player[i].connected = FALSE;
3737
3738     // in network game mode, the local player might not be the first player
3739     if (stored_player[i].connected_locally)
3740       local_player = &stored_player[i];
3741   }
3742
3743   if (!network.enabled)
3744     local_player->connected = TRUE;
3745
3746   if (tape.playing)
3747   {
3748     for (i = 0; i < MAX_PLAYERS; i++)
3749       stored_player[i].connected = tape.player_participates[i];
3750   }
3751   else if (network.enabled)
3752   {
3753     // add team mode players connected over the network (needed for correct
3754     // assignment of player figures from level to locally playing players)
3755
3756     for (i = 0; i < MAX_PLAYERS; i++)
3757       if (stored_player[i].connected_network)
3758         stored_player[i].connected = TRUE;
3759   }
3760   else if (game.team_mode)
3761   {
3762     // try to guess locally connected team mode players (needed for correct
3763     // assignment of player figures from level to locally playing players)
3764
3765     for (i = 0; i < MAX_PLAYERS; i++)
3766       if (setup.input[i].use_joystick ||
3767           setup.input[i].key.left != KSYM_UNDEFINED)
3768         stored_player[i].connected = TRUE;
3769   }
3770
3771 #if DEBUG_INIT_PLAYER
3772   DebugPrintPlayerStatus("Player status after level initialization");
3773 #endif
3774
3775 #if DEBUG_INIT_PLAYER
3776   if (options.debug)
3777     printf("Reassigning players ...\n");
3778 #endif
3779
3780   // check if any connected player was not found in playfield
3781   for (i = 0; i < MAX_PLAYERS; i++)
3782   {
3783     struct PlayerInfo *player = &stored_player[i];
3784
3785     if (player->connected && !player->present)
3786     {
3787       struct PlayerInfo *field_player = NULL;
3788
3789 #if DEBUG_INIT_PLAYER
3790       if (options.debug)
3791         printf("- looking for field player for player %d ...\n", i + 1);
3792 #endif
3793
3794       // assign first free player found that is present in the playfield
3795
3796       // first try: look for unmapped playfield player that is not connected
3797       for (j = 0; j < MAX_PLAYERS; j++)
3798         if (field_player == NULL &&
3799             stored_player[j].present &&
3800             !stored_player[j].mapped &&
3801             !stored_player[j].connected)
3802           field_player = &stored_player[j];
3803
3804       // second try: look for *any* unmapped playfield player
3805       for (j = 0; j < MAX_PLAYERS; j++)
3806         if (field_player == NULL &&
3807             stored_player[j].present &&
3808             !stored_player[j].mapped)
3809           field_player = &stored_player[j];
3810
3811       if (field_player != NULL)
3812       {
3813         int jx = field_player->jx, jy = field_player->jy;
3814
3815 #if DEBUG_INIT_PLAYER
3816         if (options.debug)
3817           printf("- found player %d\n", field_player->index_nr + 1);
3818 #endif
3819
3820         player->present = FALSE;
3821         player->active = FALSE;
3822
3823         field_player->present = TRUE;
3824         field_player->active = TRUE;
3825
3826         /*
3827         player->initial_element = field_player->initial_element;
3828         player->artwork_element = field_player->artwork_element;
3829
3830         player->block_last_field       = field_player->block_last_field;
3831         player->block_delay_adjustment = field_player->block_delay_adjustment;
3832         */
3833
3834         StorePlayer[jx][jy] = field_player->element_nr;
3835
3836         field_player->jx = field_player->last_jx = jx;
3837         field_player->jy = field_player->last_jy = jy;
3838
3839         if (local_player == player)
3840           local_player = field_player;
3841
3842         map_player_action[field_player->index_nr] = i;
3843
3844         field_player->mapped = TRUE;
3845
3846 #if DEBUG_INIT_PLAYER
3847         if (options.debug)
3848           printf("- map_player_action[%d] == %d\n",
3849                  field_player->index_nr + 1, i + 1);
3850 #endif
3851       }
3852     }
3853
3854     if (player->connected && player->present)
3855       player->mapped = TRUE;
3856   }
3857
3858 #if DEBUG_INIT_PLAYER
3859   DebugPrintPlayerStatus("Player status after player assignment (first stage)");
3860 #endif
3861
3862 #else
3863
3864   // check if any connected player was not found in playfield
3865   for (i = 0; i < MAX_PLAYERS; i++)
3866   {
3867     struct PlayerInfo *player = &stored_player[i];
3868
3869     if (player->connected && !player->present)
3870     {
3871       for (j = 0; j < MAX_PLAYERS; j++)
3872       {
3873         struct PlayerInfo *field_player = &stored_player[j];
3874         int jx = field_player->jx, jy = field_player->jy;
3875
3876         // assign first free player found that is present in the playfield
3877         if (field_player->present && !field_player->connected)
3878         {
3879           player->present = TRUE;
3880           player->active = TRUE;
3881
3882           field_player->present = FALSE;
3883           field_player->active = FALSE;
3884
3885           player->initial_element = field_player->initial_element;
3886           player->artwork_element = field_player->artwork_element;
3887
3888           player->block_last_field       = field_player->block_last_field;
3889           player->block_delay_adjustment = field_player->block_delay_adjustment;
3890
3891           StorePlayer[jx][jy] = player->element_nr;
3892
3893           player->jx = player->last_jx = jx;
3894           player->jy = player->last_jy = jy;
3895
3896           break;
3897         }
3898       }
3899     }
3900   }
3901 #endif
3902
3903 #if 0
3904   printf("::: local_player->present == %d\n", local_player->present);
3905 #endif
3906
3907   // set focus to local player for network games, else to all players
3908   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3909   game.centered_player_nr_next = game.centered_player_nr;
3910   game.set_centered_player = FALSE;
3911
3912   if (network_playing && tape.recording)
3913   {
3914     // store client dependent player focus when recording network games
3915     tape.centered_player_nr_next = game.centered_player_nr_next;
3916     tape.set_centered_player = TRUE;
3917   }
3918
3919   if (tape.playing)
3920   {
3921     // when playing a tape, eliminate all players who do not participate
3922
3923 #if USE_NEW_PLAYER_ASSIGNMENTS
3924
3925     if (!game.team_mode)
3926     {
3927       for (i = 0; i < MAX_PLAYERS; i++)
3928       {
3929         if (stored_player[i].active &&
3930             !tape.player_participates[map_player_action[i]])
3931         {
3932           struct PlayerInfo *player = &stored_player[i];
3933           int jx = player->jx, jy = player->jy;
3934
3935 #if DEBUG_INIT_PLAYER
3936           if (options.debug)
3937             printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
3938 #endif
3939
3940           player->active = FALSE;
3941           StorePlayer[jx][jy] = 0;
3942           Feld[jx][jy] = EL_EMPTY;
3943         }
3944       }
3945     }
3946
3947 #else
3948
3949     for (i = 0; i < MAX_PLAYERS; i++)
3950     {
3951       if (stored_player[i].active &&
3952           !tape.player_participates[i])
3953       {
3954         struct PlayerInfo *player = &stored_player[i];
3955         int jx = player->jx, jy = player->jy;
3956
3957         player->active = FALSE;
3958         StorePlayer[jx][jy] = 0;
3959         Feld[jx][jy] = EL_EMPTY;
3960       }
3961     }
3962 #endif
3963   }
3964   else if (!network.enabled && !game.team_mode)         // && !tape.playing
3965   {
3966     // when in single player mode, eliminate all but the local player
3967
3968     for (i = 0; i < MAX_PLAYERS; i++)
3969     {
3970       struct PlayerInfo *player = &stored_player[i];
3971
3972       if (player->active && player != local_player)
3973       {
3974         int jx = player->jx, jy = player->jy;
3975
3976         player->active = FALSE;
3977         player->present = FALSE;
3978
3979         StorePlayer[jx][jy] = 0;
3980         Feld[jx][jy] = EL_EMPTY;
3981       }
3982     }
3983   }
3984
3985   for (i = 0; i < MAX_PLAYERS; i++)
3986     if (stored_player[i].active)
3987       local_player->players_still_needed++;
3988
3989   if (level.solved_by_one_player)
3990     local_player->players_still_needed = 1;
3991
3992   // when recording the game, store which players take part in the game
3993   if (tape.recording)
3994   {
3995 #if USE_NEW_PLAYER_ASSIGNMENTS
3996     for (i = 0; i < MAX_PLAYERS; i++)
3997       if (stored_player[i].connected)
3998         tape.player_participates[i] = TRUE;
3999 #else
4000     for (i = 0; i < MAX_PLAYERS; i++)
4001       if (stored_player[i].active)
4002         tape.player_participates[i] = TRUE;
4003 #endif
4004   }
4005
4006 #if DEBUG_INIT_PLAYER
4007   DebugPrintPlayerStatus("Player status after player assignment (final stage)");
4008 #endif
4009
4010   if (BorderElement == EL_EMPTY)
4011   {
4012     SBX_Left = 0;
4013     SBX_Right = lev_fieldx - SCR_FIELDX;
4014     SBY_Upper = 0;
4015     SBY_Lower = lev_fieldy - SCR_FIELDY;
4016   }
4017   else
4018   {
4019     SBX_Left = -1;
4020     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4021     SBY_Upper = -1;
4022     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4023   }
4024
4025   if (full_lev_fieldx <= SCR_FIELDX)
4026     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4027   if (full_lev_fieldy <= SCR_FIELDY)
4028     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4029
4030   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4031     SBX_Left--;
4032   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4033     SBY_Upper--;
4034
4035   // if local player not found, look for custom element that might create
4036   // the player (make some assumptions about the right custom element)
4037   if (!local_player->present)
4038   {
4039     int start_x = 0, start_y = 0;
4040     int found_rating = 0;
4041     int found_element = EL_UNDEFINED;
4042     int player_nr = local_player->index_nr;
4043
4044     SCAN_PLAYFIELD(x, y)
4045     {
4046       int element = Feld[x][y];
4047       int content;
4048       int xx, yy;
4049       boolean is_player;
4050
4051       if (level.use_start_element[player_nr] &&
4052           level.start_element[player_nr] == element &&
4053           found_rating < 4)
4054       {
4055         start_x = x;
4056         start_y = y;
4057
4058         found_rating = 4;
4059         found_element = element;
4060       }
4061
4062       if (!IS_CUSTOM_ELEMENT(element))
4063         continue;
4064
4065       if (CAN_CHANGE(element))
4066       {
4067         for (i = 0; i < element_info[element].num_change_pages; i++)
4068         {
4069           // check for player created from custom element as single target
4070           content = element_info[element].change_page[i].target_element;
4071           is_player = ELEM_IS_PLAYER(content);
4072
4073           if (is_player && (found_rating < 3 ||
4074                             (found_rating == 3 && element < found_element)))
4075           {
4076             start_x = x;
4077             start_y = y;
4078
4079             found_rating = 3;
4080             found_element = element;
4081           }
4082         }
4083       }
4084
4085       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4086       {
4087         // check for player created from custom element as explosion content
4088         content = element_info[element].content.e[xx][yy];
4089         is_player = ELEM_IS_PLAYER(content);
4090
4091         if (is_player && (found_rating < 2 ||
4092                           (found_rating == 2 && element < found_element)))
4093         {
4094           start_x = x + xx - 1;
4095           start_y = y + yy - 1;
4096
4097           found_rating = 2;
4098           found_element = element;
4099         }
4100
4101         if (!CAN_CHANGE(element))
4102           continue;
4103
4104         for (i = 0; i < element_info[element].num_change_pages; i++)
4105         {
4106           // check for player created from custom element as extended target
4107           content =
4108             element_info[element].change_page[i].target_content.e[xx][yy];
4109
4110           is_player = ELEM_IS_PLAYER(content);
4111
4112           if (is_player && (found_rating < 1 ||
4113                             (found_rating == 1 && element < found_element)))
4114           {
4115             start_x = x + xx - 1;
4116             start_y = y + yy - 1;
4117
4118             found_rating = 1;
4119             found_element = element;
4120           }
4121         }
4122       }
4123     }
4124
4125     scroll_x = SCROLL_POSITION_X(start_x);
4126     scroll_y = SCROLL_POSITION_Y(start_y);
4127   }
4128   else
4129   {
4130     scroll_x = SCROLL_POSITION_X(local_player->jx);
4131     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4132   }
4133
4134   // !!! FIX THIS (START) !!!
4135   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4136   {
4137     InitGameEngine_EM();
4138   }
4139   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4140   {
4141     InitGameEngine_SP();
4142   }
4143   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4144   {
4145     InitGameEngine_MM();
4146   }
4147   else
4148   {
4149     DrawLevel(REDRAW_FIELD);
4150     DrawAllPlayers();
4151
4152     // after drawing the level, correct some elements
4153     if (game.timegate_time_left == 0)
4154       CloseAllOpenTimegates();
4155   }
4156
4157   // blit playfield from scroll buffer to normal back buffer for fading in
4158   BlitScreenToBitmap(backbuffer);
4159   // !!! FIX THIS (END) !!!
4160
4161   DrawMaskedBorder(fade_mask);
4162
4163   FadeIn(fade_mask);
4164
4165 #if 1
4166   // full screen redraw is required at this point in the following cases:
4167   // - special editor door undrawn when game was started from level editor
4168   // - drawing area (playfield) was changed and has to be removed completely
4169   redraw_mask = REDRAW_ALL;
4170   BackToFront();
4171 #endif
4172
4173   if (!game.restart_level)
4174   {
4175     // copy default game door content to main double buffer
4176
4177     // !!! CHECK AGAIN !!!
4178     SetPanelBackground();
4179     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4180     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4181   }
4182
4183   SetPanelBackground();
4184   SetDrawBackgroundMask(REDRAW_DOOR_1);
4185
4186   UpdateAndDisplayGameControlValues();
4187
4188   if (!game.restart_level)
4189   {
4190     UnmapGameButtons();
4191     UnmapTapeButtons();
4192
4193     FreeGameButtons();
4194     CreateGameButtons();
4195
4196     MapGameButtons();
4197     MapTapeButtons();
4198
4199     // copy actual game door content to door double buffer for OpenDoor()
4200     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4201
4202     OpenDoor(DOOR_OPEN_ALL);
4203
4204     KeyboardAutoRepeatOffUnlessAutoplay();
4205
4206 #if DEBUG_INIT_PLAYER
4207     DebugPrintPlayerStatus("Player status (final)");
4208 #endif
4209   }
4210
4211   UnmapAllGadgets();
4212
4213   MapGameButtons();
4214   MapTapeButtons();
4215
4216   if (!game.restart_level && !tape.playing)
4217   {
4218     LevelStats_incPlayed(level_nr);
4219
4220     SaveLevelSetup_SeriesInfo();
4221   }
4222
4223   game.restart_level = FALSE;
4224   game.restart_game_message = NULL;
4225   game.request_active = FALSE;
4226
4227   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4228     InitGameActions_MM();
4229
4230   SaveEngineSnapshotToListInitial();
4231
4232   if (!game.restart_level)
4233   {
4234     PlaySound(SND_GAME_STARTING);
4235
4236     if (setup.sound_music)
4237       PlayLevelMusic();
4238   }
4239 }
4240
4241 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4242                         int actual_player_x, int actual_player_y)
4243 {
4244   // this is used for non-R'n'D game engines to update certain engine values
4245
4246   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4247   {
4248     actual_player_x = correctLevelPosX_EM(actual_player_x);
4249     actual_player_y = correctLevelPosY_EM(actual_player_y);
4250   }
4251
4252   // needed to determine if sounds are played within the visible screen area
4253   scroll_x = actual_scroll_x;
4254   scroll_y = actual_scroll_y;
4255
4256   // needed to get player position for "follow finger" playing input method
4257   local_player->jx = actual_player_x;
4258   local_player->jy = actual_player_y;
4259 }
4260
4261 void InitMovDir(int x, int y)
4262 {
4263   int i, element = Feld[x][y];
4264   static int xy[4][2] =
4265   {
4266     {  0, +1 },
4267     { +1,  0 },
4268     {  0, -1 },
4269     { -1,  0 }
4270   };
4271   static int direction[3][4] =
4272   {
4273     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4274     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4275     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4276   };
4277
4278   switch (element)
4279   {
4280     case EL_BUG_RIGHT:
4281     case EL_BUG_UP:
4282     case EL_BUG_LEFT:
4283     case EL_BUG_DOWN:
4284       Feld[x][y] = EL_BUG;
4285       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4286       break;
4287
4288     case EL_SPACESHIP_RIGHT:
4289     case EL_SPACESHIP_UP:
4290     case EL_SPACESHIP_LEFT:
4291     case EL_SPACESHIP_DOWN:
4292       Feld[x][y] = EL_SPACESHIP;
4293       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4294       break;
4295
4296     case EL_BD_BUTTERFLY_RIGHT:
4297     case EL_BD_BUTTERFLY_UP:
4298     case EL_BD_BUTTERFLY_LEFT:
4299     case EL_BD_BUTTERFLY_DOWN:
4300       Feld[x][y] = EL_BD_BUTTERFLY;
4301       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4302       break;
4303
4304     case EL_BD_FIREFLY_RIGHT:
4305     case EL_BD_FIREFLY_UP:
4306     case EL_BD_FIREFLY_LEFT:
4307     case EL_BD_FIREFLY_DOWN:
4308       Feld[x][y] = EL_BD_FIREFLY;
4309       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4310       break;
4311
4312     case EL_PACMAN_RIGHT:
4313     case EL_PACMAN_UP:
4314     case EL_PACMAN_LEFT:
4315     case EL_PACMAN_DOWN:
4316       Feld[x][y] = EL_PACMAN;
4317       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4318       break;
4319
4320     case EL_YAMYAM_LEFT:
4321     case EL_YAMYAM_RIGHT:
4322     case EL_YAMYAM_UP:
4323     case EL_YAMYAM_DOWN:
4324       Feld[x][y] = EL_YAMYAM;
4325       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4326       break;
4327
4328     case EL_SP_SNIKSNAK:
4329       MovDir[x][y] = MV_UP;
4330       break;
4331
4332     case EL_SP_ELECTRON:
4333       MovDir[x][y] = MV_LEFT;
4334       break;
4335
4336     case EL_MOLE_LEFT:
4337     case EL_MOLE_RIGHT:
4338     case EL_MOLE_UP:
4339     case EL_MOLE_DOWN:
4340       Feld[x][y] = EL_MOLE;
4341       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4342       break;
4343
4344     default:
4345       if (IS_CUSTOM_ELEMENT(element))
4346       {
4347         struct ElementInfo *ei = &element_info[element];
4348         int move_direction_initial = ei->move_direction_initial;
4349         int move_pattern = ei->move_pattern;
4350
4351         if (move_direction_initial == MV_START_PREVIOUS)
4352         {
4353           if (MovDir[x][y] != MV_NONE)
4354             return;
4355
4356           move_direction_initial = MV_START_AUTOMATIC;
4357         }
4358
4359         if (move_direction_initial == MV_START_RANDOM)
4360           MovDir[x][y] = 1 << RND(4);
4361         else if (move_direction_initial & MV_ANY_DIRECTION)
4362           MovDir[x][y] = move_direction_initial;
4363         else if (move_pattern == MV_ALL_DIRECTIONS ||
4364                  move_pattern == MV_TURNING_LEFT ||
4365                  move_pattern == MV_TURNING_RIGHT ||
4366                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4367                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4368                  move_pattern == MV_TURNING_RANDOM)
4369           MovDir[x][y] = 1 << RND(4);
4370         else if (move_pattern == MV_HORIZONTAL)
4371           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4372         else if (move_pattern == MV_VERTICAL)
4373           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4374         else if (move_pattern & MV_ANY_DIRECTION)
4375           MovDir[x][y] = element_info[element].move_pattern;
4376         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4377                  move_pattern == MV_ALONG_RIGHT_SIDE)
4378         {
4379           // use random direction as default start direction
4380           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4381             MovDir[x][y] = 1 << RND(4);
4382
4383           for (i = 0; i < NUM_DIRECTIONS; i++)
4384           {
4385             int x1 = x + xy[i][0];
4386             int y1 = y + xy[i][1];
4387
4388             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4389             {
4390               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4391                 MovDir[x][y] = direction[0][i];
4392               else
4393                 MovDir[x][y] = direction[1][i];
4394
4395               break;
4396             }
4397           }
4398         }                
4399       }
4400       else
4401       {
4402         MovDir[x][y] = 1 << RND(4);
4403
4404         if (element != EL_BUG &&
4405             element != EL_SPACESHIP &&
4406             element != EL_BD_BUTTERFLY &&
4407             element != EL_BD_FIREFLY)
4408           break;
4409
4410         for (i = 0; i < NUM_DIRECTIONS; i++)
4411         {
4412           int x1 = x + xy[i][0];
4413           int y1 = y + xy[i][1];
4414
4415           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4416           {
4417             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4418             {
4419               MovDir[x][y] = direction[0][i];
4420               break;
4421             }
4422             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4423                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4424             {
4425               MovDir[x][y] = direction[1][i];
4426               break;
4427             }
4428           }
4429         }
4430       }
4431       break;
4432   }
4433
4434   GfxDir[x][y] = MovDir[x][y];
4435 }
4436
4437 void InitAmoebaNr(int x, int y)
4438 {
4439   int i;
4440   int group_nr = AmoebeNachbarNr(x, y);
4441
4442   if (group_nr == 0)
4443   {
4444     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4445     {
4446       if (AmoebaCnt[i] == 0)
4447       {
4448         group_nr = i;
4449         break;
4450       }
4451     }
4452   }
4453
4454   AmoebaNr[x][y] = group_nr;
4455   AmoebaCnt[group_nr]++;
4456   AmoebaCnt2[group_nr]++;
4457 }
4458
4459 static void PlayerWins(struct PlayerInfo *player)
4460 {
4461   if (level.game_engine_type == GAME_ENGINE_TYPE_RND &&
4462       local_player->players_still_needed > 0)
4463     return;
4464
4465   player->LevelSolved = TRUE;
4466   player->GameOver = TRUE;
4467
4468   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4469                          level.native_em_level->lev->score :
4470                          level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4471                          game_mm.score :
4472                          player->score);
4473   player->health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4474                           MM_HEALTH(game_mm.laser_overload_value) :
4475                           player->health);
4476
4477   player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed :
4478                                       TimeLeft);
4479   player->LevelSolved_CountingScore = player->score_final;
4480   player->LevelSolved_CountingHealth = player->health_final;
4481 }
4482
4483 void GameWon(void)
4484 {
4485   static int time_count_steps;
4486   static int time, time_final;
4487   static int score, score_final;
4488   static int health, health_final;
4489   static int game_over_delay_1 = 0;
4490   static int game_over_delay_2 = 0;
4491   static int game_over_delay_3 = 0;
4492   int game_over_delay_value_1 = 50;
4493   int game_over_delay_value_2 = 25;
4494   int game_over_delay_value_3 = 50;
4495
4496   if (!local_player->LevelSolved_GameWon)
4497   {
4498     int i;
4499
4500     // do not start end game actions before the player stops moving (to exit)
4501     if (local_player->MovPos)
4502       return;
4503
4504     local_player->LevelSolved_GameWon = TRUE;
4505     local_player->LevelSolved_SaveTape = tape.recording;
4506     local_player->LevelSolved_SaveScore = !tape.playing;
4507
4508     if (!tape.playing)
4509     {
4510       LevelStats_incSolved(level_nr);
4511
4512       SaveLevelSetup_SeriesInfo();
4513     }
4514
4515     if (tape.auto_play)         // tape might already be stopped here
4516       tape.auto_play_level_solved = TRUE;
4517
4518     TapeStop();
4519
4520     game_over_delay_1 = 0;
4521     game_over_delay_2 = 0;
4522     game_over_delay_3 = game_over_delay_value_3;
4523
4524     time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4525     score = score_final = local_player->score_final;
4526     health = health_final = local_player->health_final;
4527
4528     if (level.score[SC_TIME_BONUS] > 0)
4529     {
4530       if (TimeLeft > 0)
4531       {
4532         time_final = 0;
4533         score_final += TimeLeft * level.score[SC_TIME_BONUS];
4534       }
4535       else if (game.no_time_limit && TimePlayed < 999)
4536       {
4537         time_final = 999;
4538         score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4539       }
4540
4541       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4542
4543       game_over_delay_1 = game_over_delay_value_1;
4544
4545       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4546       {
4547         health_final = 0;
4548         score_final += health * level.score[SC_TIME_BONUS];
4549
4550         game_over_delay_2 = game_over_delay_value_2;
4551       }
4552
4553       local_player->score_final = score_final;
4554       local_player->health_final = health_final;
4555     }
4556
4557     if (level_editor_test_game)
4558     {
4559       time = time_final;
4560       score = score_final;
4561
4562       local_player->LevelSolved_CountingTime = time;
4563       local_player->LevelSolved_CountingScore = score;
4564
4565       game_panel_controls[GAME_PANEL_TIME].value = time;
4566       game_panel_controls[GAME_PANEL_SCORE].value = score;
4567
4568       DisplayGameControlValues();
4569     }
4570
4571     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4572     {
4573       if (ExitX >= 0 && ExitY >= 0)     // local player has left the level
4574       {
4575         // close exit door after last player
4576         if ((AllPlayersGone &&
4577              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4578               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4579               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4580             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4581             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4582         {
4583           int element = Feld[ExitX][ExitY];
4584
4585           Feld[ExitX][ExitY] =
4586             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4587              element == EL_EM_EXIT_OPEN         ? EL_EM_EXIT_CLOSING :
4588              element == EL_SP_EXIT_OPEN         ? EL_SP_EXIT_CLOSING:
4589              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4590              EL_EM_STEEL_EXIT_CLOSING);
4591
4592           PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4593         }
4594
4595         // player disappears
4596         DrawLevelField(ExitX, ExitY);
4597       }
4598
4599       for (i = 0; i < MAX_PLAYERS; i++)
4600       {
4601         struct PlayerInfo *player = &stored_player[i];
4602
4603         if (player->present)
4604         {
4605           RemovePlayer(player);
4606
4607           // player disappears
4608           DrawLevelField(player->jx, player->jy);
4609         }
4610       }
4611     }
4612
4613     PlaySound(SND_GAME_WINNING);
4614   }
4615
4616   if (game_over_delay_1 > 0)
4617   {
4618     game_over_delay_1--;
4619
4620     return;
4621   }
4622
4623   if (time != time_final)
4624   {
4625     int time_to_go = ABS(time_final - time);
4626     int time_count_dir = (time < time_final ? +1 : -1);
4627
4628     if (time_to_go < time_count_steps)
4629       time_count_steps = 1;
4630
4631     time  += time_count_steps * time_count_dir;
4632     score += time_count_steps * level.score[SC_TIME_BONUS];
4633
4634     local_player->LevelSolved_CountingTime = time;
4635     local_player->LevelSolved_CountingScore = score;
4636
4637     game_panel_controls[GAME_PANEL_TIME].value = time;
4638     game_panel_controls[GAME_PANEL_SCORE].value = score;
4639
4640     DisplayGameControlValues();
4641
4642     if (time == time_final)
4643       StopSound(SND_GAME_LEVELTIME_BONUS);
4644     else if (setup.sound_loops)
4645       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4646     else
4647       PlaySound(SND_GAME_LEVELTIME_BONUS);
4648
4649     return;
4650   }
4651
4652   if (game_over_delay_2 > 0)
4653   {
4654     game_over_delay_2--;
4655
4656     return;
4657   }
4658
4659   if (health != health_final)
4660   {
4661     int health_count_dir = (health < health_final ? +1 : -1);
4662
4663     health += health_count_dir;
4664     score  += level.score[SC_TIME_BONUS];
4665
4666     local_player->LevelSolved_CountingHealth = health;
4667     local_player->LevelSolved_CountingScore = score;
4668
4669     game_panel_controls[GAME_PANEL_HEALTH].value = health;
4670     game_panel_controls[GAME_PANEL_SCORE].value = score;
4671
4672     DisplayGameControlValues();
4673
4674     if (health == health_final)
4675       StopSound(SND_GAME_LEVELTIME_BONUS);
4676     else if (setup.sound_loops)
4677       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4678     else
4679       PlaySound(SND_GAME_LEVELTIME_BONUS);
4680
4681     return;
4682   }
4683
4684   game.panel.active = FALSE;
4685
4686   if (game_over_delay_3 > 0)
4687   {
4688     game_over_delay_3--;
4689
4690     return;
4691   }
4692
4693   GameEnd();
4694 }
4695
4696 void GameEnd(void)
4697 {
4698   // used instead of "level_nr" (needed for network games)
4699   int last_level_nr = levelset.level_nr;
4700   int hi_pos;
4701
4702   local_player->LevelSolved_GameEnd = TRUE;
4703
4704   if (local_player->LevelSolved_SaveTape)
4705   {
4706     // make sure that request dialog to save tape does not open door again
4707     if (!global.use_envelope_request)
4708       CloseDoor(DOOR_CLOSE_1);
4709
4710     SaveTapeChecked_LevelSolved(tape.level_nr);         // ask to save tape
4711   }
4712
4713   // if no tape is to be saved, close both doors simultaneously
4714   CloseDoor(DOOR_CLOSE_ALL);
4715
4716   if (level_editor_test_game)
4717   {
4718     SetGameStatus(GAME_MODE_MAIN);
4719
4720     DrawMainMenu();
4721
4722     return;
4723   }
4724
4725   if (!local_player->LevelSolved_SaveScore)
4726   {
4727     SetGameStatus(GAME_MODE_MAIN);
4728
4729     DrawMainMenu();
4730
4731     return;
4732   }
4733
4734   if (level_nr == leveldir_current->handicap_level)
4735   {
4736     leveldir_current->handicap_level++;
4737
4738     SaveLevelSetup_SeriesInfo();
4739   }
4740
4741   if (setup.increment_levels &&
4742       level_nr < leveldir_current->last_level &&
4743       !network_playing)
4744   {
4745     level_nr++;         // advance to next level
4746     TapeErase();        // start with empty tape
4747
4748     if (setup.auto_play_next_level)
4749     {
4750       LoadLevel(level_nr);
4751
4752       SaveLevelSetup_SeriesInfo();
4753     }
4754   }
4755
4756   hi_pos = NewHiScore(last_level_nr);
4757
4758   if (hi_pos >= 0 && !setup.skip_scores_after_game)
4759   {
4760     SetGameStatus(GAME_MODE_SCORES);
4761
4762     DrawHallOfFame(last_level_nr, hi_pos);
4763   }
4764   else if (setup.auto_play_next_level && setup.increment_levels &&
4765            last_level_nr < leveldir_current->last_level &&
4766            !network_playing)
4767   {
4768     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
4769   }
4770   else
4771   {
4772     SetGameStatus(GAME_MODE_MAIN);
4773
4774     DrawMainMenu();
4775   }
4776 }
4777
4778 int NewHiScore(int level_nr)
4779 {
4780   int k, l;
4781   int position = -1;
4782   boolean one_score_entry_per_name = !program.many_scores_per_name;
4783
4784   LoadScore(level_nr);
4785
4786   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4787       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
4788     return -1;
4789
4790   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
4791   {
4792     if (local_player->score_final > highscore[k].Score)
4793     {
4794       // player has made it to the hall of fame
4795
4796       if (k < MAX_SCORE_ENTRIES - 1)
4797       {
4798         int m = MAX_SCORE_ENTRIES - 1;
4799
4800         if (one_score_entry_per_name)
4801         {
4802           for (l = k; l < MAX_SCORE_ENTRIES; l++)
4803             if (strEqual(setup.player_name, highscore[l].Name))
4804               m = l;
4805
4806           if (m == k)   // player's new highscore overwrites his old one
4807             goto put_into_list;
4808         }
4809
4810         for (l = m; l > k; l--)
4811         {
4812           strcpy(highscore[l].Name, highscore[l - 1].Name);
4813           highscore[l].Score = highscore[l - 1].Score;
4814         }
4815       }
4816
4817       put_into_list:
4818
4819       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4820       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4821       highscore[k].Score = local_player->score_final; 
4822       position = k;
4823
4824       break;
4825     }
4826     else if (one_score_entry_per_name &&
4827              !strncmp(setup.player_name, highscore[k].Name,
4828                       MAX_PLAYER_NAME_LEN))
4829       break;    // player already there with a higher score
4830   }
4831
4832   if (position >= 0) 
4833     SaveScore(level_nr);
4834
4835   return position;
4836 }
4837
4838 static int getElementMoveStepsizeExt(int x, int y, int direction)
4839 {
4840   int element = Feld[x][y];
4841   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4842   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4843   int horiz_move = (dx != 0);
4844   int sign = (horiz_move ? dx : dy);
4845   int step = sign * element_info[element].move_stepsize;
4846
4847   // special values for move stepsize for spring and things on conveyor belt
4848   if (horiz_move)
4849   {
4850     if (CAN_FALL(element) &&
4851         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4852       step = sign * MOVE_STEPSIZE_NORMAL / 2;
4853     else if (element == EL_SPRING)
4854       step = sign * MOVE_STEPSIZE_NORMAL * 2;
4855   }
4856
4857   return step;
4858 }
4859
4860 static int getElementMoveStepsize(int x, int y)
4861 {
4862   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4863 }
4864
4865 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4866 {
4867   if (player->GfxAction != action || player->GfxDir != dir)
4868   {
4869     player->GfxAction = action;
4870     player->GfxDir = dir;
4871     player->Frame = 0;
4872     player->StepFrame = 0;
4873   }
4874 }
4875
4876 static void ResetGfxFrame(int x, int y)
4877 {
4878   // profiling showed that "autotest" spends 10~20% of its time in this function
4879   if (DrawingDeactivatedField())
4880     return;
4881
4882   int element = Feld[x][y];
4883   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4884
4885   if (graphic_info[graphic].anim_global_sync)
4886     GfxFrame[x][y] = FrameCounter;
4887   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4888     GfxFrame[x][y] = CustomValue[x][y];
4889   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4890     GfxFrame[x][y] = element_info[element].collect_score;
4891   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4892     GfxFrame[x][y] = ChangeDelay[x][y];
4893 }
4894
4895 static void ResetGfxAnimation(int x, int y)
4896 {
4897   GfxAction[x][y] = ACTION_DEFAULT;
4898   GfxDir[x][y] = MovDir[x][y];
4899   GfxFrame[x][y] = 0;
4900
4901   ResetGfxFrame(x, y);
4902 }
4903
4904 static void ResetRandomAnimationValue(int x, int y)
4905 {
4906   GfxRandom[x][y] = INIT_GFX_RANDOM();
4907 }
4908
4909 static void InitMovingField(int x, int y, int direction)
4910 {
4911   int element = Feld[x][y];
4912   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4913   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4914   int newx = x + dx;
4915   int newy = y + dy;
4916   boolean is_moving_before, is_moving_after;
4917
4918   // check if element was/is moving or being moved before/after mode change
4919   is_moving_before = (WasJustMoving[x][y] != 0);
4920   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
4921
4922   // reset animation only for moving elements which change direction of moving
4923   // or which just started or stopped moving
4924   // (else CEs with property "can move" / "not moving" are reset each frame)
4925   if (is_moving_before != is_moving_after ||
4926       direction != MovDir[x][y])
4927     ResetGfxAnimation(x, y);
4928
4929   MovDir[x][y] = direction;
4930   GfxDir[x][y] = direction;
4931
4932   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4933                      direction == MV_DOWN && CAN_FALL(element) ?
4934                      ACTION_FALLING : ACTION_MOVING);
4935
4936   // this is needed for CEs with property "can move" / "not moving"
4937
4938   if (is_moving_after)
4939   {
4940     if (Feld[newx][newy] == EL_EMPTY)
4941       Feld[newx][newy] = EL_BLOCKED;
4942
4943     MovDir[newx][newy] = MovDir[x][y];
4944
4945     CustomValue[newx][newy] = CustomValue[x][y];
4946
4947     GfxFrame[newx][newy] = GfxFrame[x][y];
4948     GfxRandom[newx][newy] = GfxRandom[x][y];
4949     GfxAction[newx][newy] = GfxAction[x][y];
4950     GfxDir[newx][newy] = GfxDir[x][y];
4951   }
4952 }
4953
4954 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4955 {
4956   int direction = MovDir[x][y];
4957   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4958   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
4959
4960   *goes_to_x = newx;
4961   *goes_to_y = newy;
4962 }
4963
4964 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4965 {
4966   int oldx = x, oldy = y;
4967   int direction = MovDir[x][y];
4968
4969   if (direction == MV_LEFT)
4970     oldx++;
4971   else if (direction == MV_RIGHT)
4972     oldx--;
4973   else if (direction == MV_UP)
4974     oldy++;
4975   else if (direction == MV_DOWN)
4976     oldy--;
4977
4978   *comes_from_x = oldx;
4979   *comes_from_y = oldy;
4980 }
4981
4982 static int MovingOrBlocked2Element(int x, int y)
4983 {
4984   int element = Feld[x][y];
4985
4986   if (element == EL_BLOCKED)
4987   {
4988     int oldx, oldy;
4989
4990     Blocked2Moving(x, y, &oldx, &oldy);
4991     return Feld[oldx][oldy];
4992   }
4993   else
4994     return element;
4995 }
4996
4997 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
4998 {
4999   // like MovingOrBlocked2Element(), but if element is moving
5000   // and (x,y) is the field the moving element is just leaving,
5001   // return EL_BLOCKED instead of the element value
5002   int element = Feld[x][y];
5003
5004   if (IS_MOVING(x, y))
5005   {
5006     if (element == EL_BLOCKED)
5007     {
5008       int oldx, oldy;
5009
5010       Blocked2Moving(x, y, &oldx, &oldy);
5011       return Feld[oldx][oldy];
5012     }
5013     else
5014       return EL_BLOCKED;
5015   }
5016   else
5017     return element;
5018 }
5019
5020 static void RemoveField(int x, int y)
5021 {
5022   Feld[x][y] = EL_EMPTY;
5023
5024   MovPos[x][y] = 0;
5025   MovDir[x][y] = 0;
5026   MovDelay[x][y] = 0;
5027
5028   CustomValue[x][y] = 0;
5029
5030   AmoebaNr[x][y] = 0;
5031   ChangeDelay[x][y] = 0;
5032   ChangePage[x][y] = -1;
5033   Pushed[x][y] = FALSE;
5034
5035   GfxElement[x][y] = EL_UNDEFINED;
5036   GfxAction[x][y] = ACTION_DEFAULT;
5037   GfxDir[x][y] = MV_NONE;
5038 }
5039
5040 static void RemoveMovingField(int x, int y)
5041 {
5042   int oldx = x, oldy = y, newx = x, newy = y;
5043   int element = Feld[x][y];
5044   int next_element = EL_UNDEFINED;
5045
5046   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5047     return;
5048
5049   if (IS_MOVING(x, y))
5050   {
5051     Moving2Blocked(x, y, &newx, &newy);
5052
5053     if (Feld[newx][newy] != EL_BLOCKED)
5054     {
5055       // element is moving, but target field is not free (blocked), but
5056       // already occupied by something different (example: acid pool);
5057       // in this case, only remove the moving field, but not the target
5058
5059       RemoveField(oldx, oldy);
5060
5061       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5062
5063       TEST_DrawLevelField(oldx, oldy);
5064
5065       return;
5066     }
5067   }
5068   else if (element == EL_BLOCKED)
5069   {
5070     Blocked2Moving(x, y, &oldx, &oldy);
5071     if (!IS_MOVING(oldx, oldy))
5072       return;
5073   }
5074
5075   if (element == EL_BLOCKED &&
5076       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5077        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5078        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5079        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5080        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5081        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5082     next_element = get_next_element(Feld[oldx][oldy]);
5083
5084   RemoveField(oldx, oldy);
5085   RemoveField(newx, newy);
5086
5087   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5088
5089   if (next_element != EL_UNDEFINED)
5090     Feld[oldx][oldy] = next_element;
5091
5092   TEST_DrawLevelField(oldx, oldy);
5093   TEST_DrawLevelField(newx, newy);
5094 }
5095
5096 void DrawDynamite(int x, int y)
5097 {
5098   int sx = SCREENX(x), sy = SCREENY(y);
5099   int graphic = el2img(Feld[x][y]);
5100   int frame;
5101
5102   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5103     return;
5104
5105   if (IS_WALKABLE_INSIDE(Back[x][y]))
5106     return;
5107
5108   if (Back[x][y])
5109     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5110   else if (Store[x][y])
5111     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5112
5113   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5114
5115   if (Back[x][y] || Store[x][y])
5116     DrawGraphicThruMask(sx, sy, graphic, frame);
5117   else
5118     DrawGraphic(sx, sy, graphic, frame);
5119 }
5120
5121 static void CheckDynamite(int x, int y)
5122 {
5123   if (MovDelay[x][y] != 0)      // dynamite is still waiting to explode
5124   {
5125     MovDelay[x][y]--;
5126
5127     if (MovDelay[x][y] != 0)
5128     {
5129       DrawDynamite(x, y);
5130       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5131
5132       return;
5133     }
5134   }
5135
5136   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5137
5138   Bang(x, y);
5139 }
5140
5141 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5142 {
5143   boolean num_checked_players = 0;
5144   int i;
5145
5146   for (i = 0; i < MAX_PLAYERS; i++)
5147   {
5148     if (stored_player[i].active)
5149     {
5150       int sx = stored_player[i].jx;
5151       int sy = stored_player[i].jy;
5152
5153       if (num_checked_players == 0)
5154       {
5155         *sx1 = *sx2 = sx;
5156         *sy1 = *sy2 = sy;
5157       }
5158       else
5159       {
5160         *sx1 = MIN(*sx1, sx);
5161         *sy1 = MIN(*sy1, sy);
5162         *sx2 = MAX(*sx2, sx);
5163         *sy2 = MAX(*sy2, sy);
5164       }
5165
5166       num_checked_players++;
5167     }
5168   }
5169 }
5170
5171 static boolean checkIfAllPlayersFitToScreen_RND(void)
5172 {
5173   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5174
5175   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5176
5177   return (sx2 - sx1 < SCR_FIELDX &&
5178           sy2 - sy1 < SCR_FIELDY);
5179 }
5180
5181 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5182 {
5183   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5184
5185   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5186
5187   *sx = (sx1 + sx2) / 2;
5188   *sy = (sy1 + sy2) / 2;
5189 }
5190
5191 static void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5192                                boolean center_screen, boolean quick_relocation)
5193 {
5194   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5195   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5196   boolean no_delay = (tape.warp_forward);
5197   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5198   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5199   int new_scroll_x, new_scroll_y;
5200
5201   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5202   {
5203     // case 1: quick relocation inside visible screen (without scrolling)
5204
5205     RedrawPlayfield();
5206
5207     return;
5208   }
5209
5210   if (!level.shifted_relocation || center_screen)
5211   {
5212     // relocation _with_ centering of screen
5213
5214     new_scroll_x = SCROLL_POSITION_X(x);
5215     new_scroll_y = SCROLL_POSITION_Y(y);
5216   }
5217   else
5218   {
5219     // relocation _without_ centering of screen
5220
5221     int center_scroll_x = SCROLL_POSITION_X(old_x);
5222     int center_scroll_y = SCROLL_POSITION_Y(old_y);
5223     int offset_x = x + (scroll_x - center_scroll_x);
5224     int offset_y = y + (scroll_y - center_scroll_y);
5225
5226     // for new screen position, apply previous offset to center position
5227     new_scroll_x = SCROLL_POSITION_X(offset_x);
5228     new_scroll_y = SCROLL_POSITION_Y(offset_y);
5229   }
5230
5231   if (quick_relocation)
5232   {
5233     // case 2: quick relocation (redraw without visible scrolling)
5234
5235     scroll_x = new_scroll_x;
5236     scroll_y = new_scroll_y;
5237
5238     RedrawPlayfield();
5239
5240     return;
5241   }
5242
5243   // case 3: visible relocation (with scrolling to new position)
5244
5245   ScrollScreen(NULL, SCROLL_GO_ON);     // scroll last frame to full tile
5246
5247   SetVideoFrameDelay(wait_delay_value);
5248
5249   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5250   {
5251     int dx = 0, dy = 0;
5252     int fx = FX, fy = FY;
5253
5254     dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5255     dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5256
5257     if (dx == 0 && dy == 0)             // no scrolling needed at all
5258       break;
5259
5260     scroll_x -= dx;
5261     scroll_y -= dy;
5262
5263     fx += dx * TILEX / 2;
5264     fy += dy * TILEY / 2;
5265
5266     ScrollLevel(dx, dy);
5267     DrawAllPlayers();
5268
5269     // scroll in two steps of half tile size to make things smoother
5270     BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5271
5272     // scroll second step to align at full tile size
5273     BlitScreenToBitmap(window);
5274   }
5275
5276   DrawAllPlayers();
5277   BackToFront();
5278
5279   SetVideoFrameDelay(frame_delay_value_old);
5280 }
5281
5282 static void RelocatePlayer(int jx, int jy, int el_player_raw)
5283 {
5284   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5285   int player_nr = GET_PLAYER_NR(el_player);
5286   struct PlayerInfo *player = &stored_player[player_nr];
5287   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5288   boolean no_delay = (tape.warp_forward);
5289   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5290   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5291   int old_jx = player->jx;
5292   int old_jy = player->jy;
5293   int old_element = Feld[old_jx][old_jy];
5294   int element = Feld[jx][jy];
5295   boolean player_relocated = (old_jx != jx || old_jy != jy);
5296
5297   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5298   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5299   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5300   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5301   int leave_side_horiz = move_dir_horiz;
5302   int leave_side_vert  = move_dir_vert;
5303   int enter_side = enter_side_horiz | enter_side_vert;
5304   int leave_side = leave_side_horiz | leave_side_vert;
5305
5306   if (player->GameOver)         // do not reanimate dead player
5307     return;
5308
5309   if (!player_relocated)        // no need to relocate the player
5310     return;
5311
5312   if (IS_PLAYER(jx, jy))        // player already placed at new position
5313   {
5314     RemoveField(jx, jy);        // temporarily remove newly placed player
5315     DrawLevelField(jx, jy);
5316   }
5317
5318   if (player->present)
5319   {
5320     while (player->MovPos)
5321     {
5322       ScrollPlayer(player, SCROLL_GO_ON);
5323       ScrollScreen(NULL, SCROLL_GO_ON);
5324
5325       AdvanceFrameAndPlayerCounters(player->index_nr);
5326
5327       DrawPlayer(player);
5328
5329       BackToFront_WithFrameDelay(wait_delay_value);
5330     }
5331
5332     DrawPlayer(player);         // needed here only to cleanup last field
5333     DrawLevelField(player->jx, player->jy);     // remove player graphic
5334
5335     player->is_moving = FALSE;
5336   }
5337
5338   if (IS_CUSTOM_ELEMENT(old_element))
5339     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5340                                CE_LEFT_BY_PLAYER,
5341                                player->index_bit, leave_side);
5342
5343   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5344                                       CE_PLAYER_LEAVES_X,
5345                                       player->index_bit, leave_side);
5346
5347   Feld[jx][jy] = el_player;
5348   InitPlayerField(jx, jy, el_player, TRUE);
5349
5350   /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5351      possible that the relocation target field did not contain a player element,
5352      but a walkable element, to which the new player was relocated -- in this
5353      case, restore that (already initialized!) element on the player field */
5354   if (!ELEM_IS_PLAYER(element)) // player may be set on walkable element
5355   {
5356     Feld[jx][jy] = element;     // restore previously existing element
5357   }
5358
5359   // only visually relocate centered player
5360   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5361                      FALSE, level.instant_relocation);
5362
5363   TestIfPlayerTouchesBadThing(jx, jy);
5364   TestIfPlayerTouchesCustomElement(jx, jy);
5365
5366   if (IS_CUSTOM_ELEMENT(element))
5367     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5368                                player->index_bit, enter_side);
5369
5370   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5371                                       player->index_bit, enter_side);
5372
5373   if (player->is_switching)
5374   {
5375     /* ensure that relocation while still switching an element does not cause
5376        a new element to be treated as also switched directly after relocation
5377        (this is important for teleporter switches that teleport the player to
5378        a place where another teleporter switch is in the same direction, which
5379        would then incorrectly be treated as immediately switched before the
5380        direction key that caused the switch was released) */
5381
5382     player->switch_x += jx - old_jx;
5383     player->switch_y += jy - old_jy;
5384   }
5385 }
5386
5387 static void Explode(int ex, int ey, int phase, int mode)
5388 {
5389   int x, y;
5390   int last_phase;
5391   int border_element;
5392
5393   // !!! eliminate this variable !!!
5394   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5395
5396   if (game.explosions_delayed)
5397   {
5398     ExplodeField[ex][ey] = mode;
5399     return;
5400   }
5401
5402   if (phase == EX_PHASE_START)          // initialize 'Store[][]' field
5403   {
5404     int center_element = Feld[ex][ey];
5405     int artwork_element, explosion_element;     // set these values later
5406
5407     // remove things displayed in background while burning dynamite
5408     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5409       Back[ex][ey] = 0;
5410
5411     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5412     {
5413       // put moving element to center field (and let it explode there)
5414       center_element = MovingOrBlocked2Element(ex, ey);
5415       RemoveMovingField(ex, ey);
5416       Feld[ex][ey] = center_element;
5417     }
5418
5419     // now "center_element" is finally determined -- set related values now
5420     artwork_element = center_element;           // for custom player artwork
5421     explosion_element = center_element;         // for custom player artwork
5422
5423     if (IS_PLAYER(ex, ey))
5424     {
5425       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5426
5427       artwork_element = stored_player[player_nr].artwork_element;
5428
5429       if (level.use_explosion_element[player_nr])
5430       {
5431         explosion_element = level.explosion_element[player_nr];
5432         artwork_element = explosion_element;
5433       }
5434     }
5435
5436     if (mode == EX_TYPE_NORMAL ||
5437         mode == EX_TYPE_CENTER ||
5438         mode == EX_TYPE_CROSS)
5439       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5440
5441     last_phase = element_info[explosion_element].explosion_delay + 1;
5442
5443     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5444     {
5445       int xx = x - ex + 1;
5446       int yy = y - ey + 1;
5447       int element;
5448
5449       if (!IN_LEV_FIELD(x, y) ||
5450           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5451           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5452         continue;
5453
5454       element = Feld[x][y];
5455
5456       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5457       {
5458         element = MovingOrBlocked2Element(x, y);
5459
5460         if (!IS_EXPLOSION_PROOF(element))
5461           RemoveMovingField(x, y);
5462       }
5463
5464       // indestructible elements can only explode in center (but not flames)
5465       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5466                                            mode == EX_TYPE_BORDER)) ||
5467           element == EL_FLAMES)
5468         continue;
5469
5470       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5471          behaviour, for example when touching a yamyam that explodes to rocks
5472          with active deadly shield, a rock is created under the player !!! */
5473       // (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8)
5474 #if 0
5475       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5476           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5477            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5478 #else
5479       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5480 #endif
5481       {
5482         if (IS_ACTIVE_BOMB(element))
5483         {
5484           // re-activate things under the bomb like gate or penguin
5485           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5486           Back[x][y] = 0;
5487         }
5488
5489         continue;
5490       }
5491
5492       // save walkable background elements while explosion on same tile
5493       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5494           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5495         Back[x][y] = element;
5496
5497       // ignite explodable elements reached by other explosion
5498       if (element == EL_EXPLOSION)
5499         element = Store2[x][y];
5500
5501       if (AmoebaNr[x][y] &&
5502           (element == EL_AMOEBA_FULL ||
5503            element == EL_BD_AMOEBA ||
5504            element == EL_AMOEBA_GROWING))
5505       {
5506         AmoebaCnt[AmoebaNr[x][y]]--;
5507         AmoebaCnt2[AmoebaNr[x][y]]--;
5508       }
5509
5510       RemoveField(x, y);
5511
5512       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5513       {
5514         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5515
5516         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5517
5518         if (PLAYERINFO(ex, ey)->use_murphy)
5519           Store[x][y] = EL_EMPTY;
5520       }
5521
5522       // !!! check this case -- currently needed for rnd_rado_negundo_v,
5523       // !!! levels 015 018 019 020 021 022 023 026 027 028 !!!
5524       else if (ELEM_IS_PLAYER(center_element))
5525         Store[x][y] = EL_EMPTY;
5526       else if (center_element == EL_YAMYAM)
5527         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5528       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5529         Store[x][y] = element_info[center_element].content.e[xx][yy];
5530 #if 1
5531       // needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5532       // (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5533       // otherwise) -- FIX THIS !!!
5534       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5535         Store[x][y] = element_info[element].content.e[1][1];
5536 #else
5537       else if (!CAN_EXPLODE(element))
5538         Store[x][y] = element_info[element].content.e[1][1];
5539 #endif
5540       else
5541         Store[x][y] = EL_EMPTY;
5542
5543       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5544           center_element == EL_AMOEBA_TO_DIAMOND)
5545         Store2[x][y] = element;
5546
5547       Feld[x][y] = EL_EXPLOSION;
5548       GfxElement[x][y] = artwork_element;
5549
5550       ExplodePhase[x][y] = 1;
5551       ExplodeDelay[x][y] = last_phase;
5552
5553       Stop[x][y] = TRUE;
5554     }
5555
5556     if (center_element == EL_YAMYAM)
5557       game.yamyam_content_nr =
5558         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5559
5560     return;
5561   }
5562
5563   if (Stop[ex][ey])
5564     return;
5565
5566   x = ex;
5567   y = ey;
5568
5569   if (phase == 1)
5570     GfxFrame[x][y] = 0;         // restart explosion animation
5571
5572   last_phase = ExplodeDelay[x][y];
5573
5574   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5575
5576   // this can happen if the player leaves an explosion just in time
5577   if (GfxElement[x][y] == EL_UNDEFINED)
5578     GfxElement[x][y] = EL_EMPTY;
5579
5580   border_element = Store2[x][y];
5581   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5582     border_element = StorePlayer[x][y];
5583
5584   if (phase == element_info[border_element].ignition_delay ||
5585       phase == last_phase)
5586   {
5587     boolean border_explosion = FALSE;
5588
5589     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5590         !PLAYER_EXPLOSION_PROTECTED(x, y))
5591     {
5592       KillPlayerUnlessExplosionProtected(x, y);
5593       border_explosion = TRUE;
5594     }
5595     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5596     {
5597       Feld[x][y] = Store2[x][y];
5598       Store2[x][y] = 0;
5599       Bang(x, y);
5600       border_explosion = TRUE;
5601     }
5602     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5603     {
5604       AmoebeUmwandeln(x, y);
5605       Store2[x][y] = 0;
5606       border_explosion = TRUE;
5607     }
5608
5609     // if an element just explodes due to another explosion (chain-reaction),
5610     // do not immediately end the new explosion when it was the last frame of
5611     // the explosion (as it would be done in the following "if"-statement!)
5612     if (border_explosion && phase == last_phase)
5613       return;
5614   }
5615
5616   if (phase == last_phase)
5617   {
5618     int element;
5619
5620     element = Feld[x][y] = Store[x][y];
5621     Store[x][y] = Store2[x][y] = 0;
5622     GfxElement[x][y] = EL_UNDEFINED;
5623
5624     // player can escape from explosions and might therefore be still alive
5625     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5626         element <= EL_PLAYER_IS_EXPLODING_4)
5627     {
5628       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5629       int explosion_element = EL_PLAYER_1 + player_nr;
5630       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5631       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5632
5633       if (level.use_explosion_element[player_nr])
5634         explosion_element = level.explosion_element[player_nr];
5635
5636       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5637                     element_info[explosion_element].content.e[xx][yy]);
5638     }
5639
5640     // restore probably existing indestructible background element
5641     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5642       element = Feld[x][y] = Back[x][y];
5643     Back[x][y] = 0;
5644
5645     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5646     GfxDir[x][y] = MV_NONE;
5647     ChangeDelay[x][y] = 0;
5648     ChangePage[x][y] = -1;
5649
5650     CustomValue[x][y] = 0;
5651
5652     InitField_WithBug2(x, y, FALSE);
5653
5654     TEST_DrawLevelField(x, y);
5655
5656     TestIfElementTouchesCustomElement(x, y);
5657
5658     if (GFX_CRUMBLED(element))
5659       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5660
5661     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5662       StorePlayer[x][y] = 0;
5663
5664     if (ELEM_IS_PLAYER(element))
5665       RelocatePlayer(x, y, element);
5666   }
5667   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5668   {
5669     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5670     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5671
5672     if (phase == delay)
5673       TEST_DrawLevelFieldCrumbled(x, y);
5674
5675     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5676     {
5677       DrawLevelElement(x, y, Back[x][y]);
5678       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5679     }
5680     else if (IS_WALKABLE_UNDER(Back[x][y]))
5681     {
5682       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5683       DrawLevelElementThruMask(x, y, Back[x][y]);
5684     }
5685     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5686       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5687   }
5688 }
5689
5690 static void DynaExplode(int ex, int ey)
5691 {
5692   int i, j;
5693   int dynabomb_element = Feld[ex][ey];
5694   int dynabomb_size = 1;
5695   boolean dynabomb_xl = FALSE;
5696   struct PlayerInfo *player;
5697   static int xy[4][2] =
5698   {
5699     { 0, -1 },
5700     { -1, 0 },
5701     { +1, 0 },
5702     { 0, +1 }
5703   };
5704
5705   if (IS_ACTIVE_BOMB(dynabomb_element))
5706   {
5707     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5708     dynabomb_size = player->dynabomb_size;
5709     dynabomb_xl = player->dynabomb_xl;
5710     player->dynabombs_left++;
5711   }
5712
5713   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5714
5715   for (i = 0; i < NUM_DIRECTIONS; i++)
5716   {
5717     for (j = 1; j <= dynabomb_size; j++)
5718     {
5719       int x = ex + j * xy[i][0];
5720       int y = ey + j * xy[i][1];
5721       int element;
5722
5723       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5724         break;
5725
5726       element = Feld[x][y];
5727
5728       // do not restart explosions of fields with active bombs
5729       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5730         continue;
5731
5732       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5733
5734       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5735           !IS_DIGGABLE(element) && !dynabomb_xl)
5736         break;
5737     }
5738   }
5739 }
5740
5741 void Bang(int x, int y)
5742 {
5743   int element = MovingOrBlocked2Element(x, y);
5744   int explosion_type = EX_TYPE_NORMAL;
5745
5746   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5747   {
5748     struct PlayerInfo *player = PLAYERINFO(x, y);
5749
5750     element = Feld[x][y] = player->initial_element;
5751
5752     if (level.use_explosion_element[player->index_nr])
5753     {
5754       int explosion_element = level.explosion_element[player->index_nr];
5755
5756       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5757         explosion_type = EX_TYPE_CROSS;
5758       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5759         explosion_type = EX_TYPE_CENTER;
5760     }
5761   }
5762
5763   switch (element)
5764   {
5765     case EL_BUG:
5766     case EL_SPACESHIP:
5767     case EL_BD_BUTTERFLY:
5768     case EL_BD_FIREFLY:
5769     case EL_YAMYAM:
5770     case EL_DARK_YAMYAM:
5771     case EL_ROBOT:
5772     case EL_PACMAN:
5773     case EL_MOLE:
5774       RaiseScoreElement(element);
5775       break;
5776
5777     case EL_DYNABOMB_PLAYER_1_ACTIVE:
5778     case EL_DYNABOMB_PLAYER_2_ACTIVE:
5779     case EL_DYNABOMB_PLAYER_3_ACTIVE:
5780     case EL_DYNABOMB_PLAYER_4_ACTIVE:
5781     case EL_DYNABOMB_INCREASE_NUMBER:
5782     case EL_DYNABOMB_INCREASE_SIZE:
5783     case EL_DYNABOMB_INCREASE_POWER:
5784       explosion_type = EX_TYPE_DYNA;
5785       break;
5786
5787     case EL_DC_LANDMINE:
5788       explosion_type = EX_TYPE_CENTER;
5789       break;
5790
5791     case EL_PENGUIN:
5792     case EL_LAMP:
5793     case EL_LAMP_ACTIVE:
5794     case EL_AMOEBA_TO_DIAMOND:
5795       if (!IS_PLAYER(x, y))     // penguin and player may be at same field
5796         explosion_type = EX_TYPE_CENTER;
5797       break;
5798
5799     default:
5800       if (element_info[element].explosion_type == EXPLODES_CROSS)
5801         explosion_type = EX_TYPE_CROSS;
5802       else if (element_info[element].explosion_type == EXPLODES_1X1)
5803         explosion_type = EX_TYPE_CENTER;
5804       break;
5805   }
5806
5807   if (explosion_type == EX_TYPE_DYNA)
5808     DynaExplode(x, y);
5809   else
5810     Explode(x, y, EX_PHASE_START, explosion_type);
5811
5812   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5813 }
5814
5815 static void SplashAcid(int x, int y)
5816 {
5817   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5818       (!IN_LEV_FIELD(x - 1, y - 2) ||
5819        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5820     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5821
5822   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5823       (!IN_LEV_FIELD(x + 1, y - 2) ||
5824        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5825     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5826
5827   PlayLevelSound(x, y, SND_ACID_SPLASHING);
5828 }
5829
5830 static void InitBeltMovement(void)
5831 {
5832   static int belt_base_element[4] =
5833   {
5834     EL_CONVEYOR_BELT_1_LEFT,
5835     EL_CONVEYOR_BELT_2_LEFT,
5836     EL_CONVEYOR_BELT_3_LEFT,
5837     EL_CONVEYOR_BELT_4_LEFT
5838   };
5839   static int belt_base_active_element[4] =
5840   {
5841     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5842     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5843     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5844     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5845   };
5846
5847   int x, y, i, j;
5848
5849   // set frame order for belt animation graphic according to belt direction
5850   for (i = 0; i < NUM_BELTS; i++)
5851   {
5852     int belt_nr = i;
5853
5854     for (j = 0; j < NUM_BELT_PARTS; j++)
5855     {
5856       int element = belt_base_active_element[belt_nr] + j;
5857       int graphic_1 = el2img(element);
5858       int graphic_2 = el2panelimg(element);
5859
5860       if (game.belt_dir[i] == MV_LEFT)
5861       {
5862         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5863         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5864       }
5865       else
5866       {
5867         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5868         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5869       }
5870     }
5871   }
5872
5873   SCAN_PLAYFIELD(x, y)
5874   {
5875     int element = Feld[x][y];
5876
5877     for (i = 0; i < NUM_BELTS; i++)
5878     {
5879       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5880       {
5881         int e_belt_nr = getBeltNrFromBeltElement(element);
5882         int belt_nr = i;
5883
5884         if (e_belt_nr == belt_nr)
5885         {
5886           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5887
5888           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5889         }
5890       }
5891     }
5892   }
5893 }
5894
5895 static void ToggleBeltSwitch(int x, int y)
5896 {
5897   static int belt_base_element[4] =
5898   {
5899     EL_CONVEYOR_BELT_1_LEFT,
5900     EL_CONVEYOR_BELT_2_LEFT,
5901     EL_CONVEYOR_BELT_3_LEFT,
5902     EL_CONVEYOR_BELT_4_LEFT
5903   };
5904   static int belt_base_active_element[4] =
5905   {
5906     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5907     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5908     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5909     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5910   };
5911   static int belt_base_switch_element[4] =
5912   {
5913     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5914     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5915     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5916     EL_CONVEYOR_BELT_4_SWITCH_LEFT
5917   };
5918   static int belt_move_dir[4] =
5919   {
5920     MV_LEFT,
5921     MV_NONE,
5922     MV_RIGHT,
5923     MV_NONE,
5924   };
5925
5926   int element = Feld[x][y];
5927   int belt_nr = getBeltNrFromBeltSwitchElement(element);
5928   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5929   int belt_dir = belt_move_dir[belt_dir_nr];
5930   int xx, yy, i;
5931
5932   if (!IS_BELT_SWITCH(element))
5933     return;
5934
5935   game.belt_dir_nr[belt_nr] = belt_dir_nr;
5936   game.belt_dir[belt_nr] = belt_dir;
5937
5938   if (belt_dir_nr == 3)
5939     belt_dir_nr = 1;
5940
5941   // set frame order for belt animation graphic according to belt direction
5942   for (i = 0; i < NUM_BELT_PARTS; i++)
5943   {
5944     int element = belt_base_active_element[belt_nr] + i;
5945     int graphic_1 = el2img(element);
5946     int graphic_2 = el2panelimg(element);
5947
5948     if (belt_dir == MV_LEFT)
5949     {
5950       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5951       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5952     }
5953     else
5954     {
5955       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5956       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5957     }
5958   }
5959
5960   SCAN_PLAYFIELD(xx, yy)
5961   {
5962     int element = Feld[xx][yy];
5963
5964     if (IS_BELT_SWITCH(element))
5965     {
5966       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5967
5968       if (e_belt_nr == belt_nr)
5969       {
5970         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5971         TEST_DrawLevelField(xx, yy);
5972       }
5973     }
5974     else if (IS_BELT(element) && belt_dir != MV_NONE)
5975     {
5976       int e_belt_nr = getBeltNrFromBeltElement(element);
5977
5978       if (e_belt_nr == belt_nr)
5979       {
5980         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
5981
5982         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
5983         TEST_DrawLevelField(xx, yy);
5984       }
5985     }
5986     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
5987     {
5988       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
5989
5990       if (e_belt_nr == belt_nr)
5991       {
5992         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
5993
5994         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
5995         TEST_DrawLevelField(xx, yy);
5996       }
5997     }
5998   }
5999 }
6000
6001 static void ToggleSwitchgateSwitch(int x, int y)
6002 {
6003   int xx, yy;
6004
6005   game.switchgate_pos = !game.switchgate_pos;
6006
6007   SCAN_PLAYFIELD(xx, yy)
6008   {
6009     int element = Feld[xx][yy];
6010
6011     if (element == EL_SWITCHGATE_SWITCH_UP)
6012     {
6013       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6014       TEST_DrawLevelField(xx, yy);
6015     }
6016     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6017     {
6018       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6019       TEST_DrawLevelField(xx, yy);
6020     }
6021     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6022     {
6023       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6024       TEST_DrawLevelField(xx, yy);
6025     }
6026     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6027     {
6028       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6029       TEST_DrawLevelField(xx, yy);
6030     }
6031     else if (element == EL_SWITCHGATE_OPEN ||
6032              element == EL_SWITCHGATE_OPENING)
6033     {
6034       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6035
6036       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6037     }
6038     else if (element == EL_SWITCHGATE_CLOSED ||
6039              element == EL_SWITCHGATE_CLOSING)
6040     {
6041       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6042
6043       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6044     }
6045   }
6046 }
6047
6048 static int getInvisibleActiveFromInvisibleElement(int element)
6049 {
6050   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6051           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6052           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6053           element);
6054 }
6055
6056 static int getInvisibleFromInvisibleActiveElement(int element)
6057 {
6058   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6059           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6060           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6061           element);
6062 }
6063
6064 static void RedrawAllLightSwitchesAndInvisibleElements(void)
6065 {
6066   int x, y;
6067
6068   SCAN_PLAYFIELD(x, y)
6069   {
6070     int element = Feld[x][y];
6071
6072     if (element == EL_LIGHT_SWITCH &&
6073         game.light_time_left > 0)
6074     {
6075       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6076       TEST_DrawLevelField(x, y);
6077     }
6078     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6079              game.light_time_left == 0)
6080     {
6081       Feld[x][y] = EL_LIGHT_SWITCH;
6082       TEST_DrawLevelField(x, y);
6083     }
6084     else if (element == EL_EMC_DRIPPER &&
6085              game.light_time_left > 0)
6086     {
6087       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6088       TEST_DrawLevelField(x, y);
6089     }
6090     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6091              game.light_time_left == 0)
6092     {
6093       Feld[x][y] = EL_EMC_DRIPPER;
6094       TEST_DrawLevelField(x, y);
6095     }
6096     else if (element == EL_INVISIBLE_STEELWALL ||
6097              element == EL_INVISIBLE_WALL ||
6098              element == EL_INVISIBLE_SAND)
6099     {
6100       if (game.light_time_left > 0)
6101         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6102
6103       TEST_DrawLevelField(x, y);
6104
6105       // uncrumble neighbour fields, if needed
6106       if (element == EL_INVISIBLE_SAND)
6107         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6108     }
6109     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6110              element == EL_INVISIBLE_WALL_ACTIVE ||
6111              element == EL_INVISIBLE_SAND_ACTIVE)
6112     {
6113       if (game.light_time_left == 0)
6114         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6115
6116       TEST_DrawLevelField(x, y);
6117
6118       // re-crumble neighbour fields, if needed
6119       if (element == EL_INVISIBLE_SAND)
6120         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6121     }
6122   }
6123 }
6124
6125 static void RedrawAllInvisibleElementsForLenses(void)
6126 {
6127   int x, y;
6128
6129   SCAN_PLAYFIELD(x, y)
6130   {
6131     int element = Feld[x][y];
6132
6133     if (element == EL_EMC_DRIPPER &&
6134         game.lenses_time_left > 0)
6135     {
6136       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6137       TEST_DrawLevelField(x, y);
6138     }
6139     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6140              game.lenses_time_left == 0)
6141     {
6142       Feld[x][y] = EL_EMC_DRIPPER;
6143       TEST_DrawLevelField(x, y);
6144     }
6145     else if (element == EL_INVISIBLE_STEELWALL ||
6146              element == EL_INVISIBLE_WALL ||
6147              element == EL_INVISIBLE_SAND)
6148     {
6149       if (game.lenses_time_left > 0)
6150         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6151
6152       TEST_DrawLevelField(x, y);
6153
6154       // uncrumble neighbour fields, if needed
6155       if (element == EL_INVISIBLE_SAND)
6156         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6157     }
6158     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6159              element == EL_INVISIBLE_WALL_ACTIVE ||
6160              element == EL_INVISIBLE_SAND_ACTIVE)
6161     {
6162       if (game.lenses_time_left == 0)
6163         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6164
6165       TEST_DrawLevelField(x, y);
6166
6167       // re-crumble neighbour fields, if needed
6168       if (element == EL_INVISIBLE_SAND)
6169         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6170     }
6171   }
6172 }
6173
6174 static void RedrawAllInvisibleElementsForMagnifier(void)
6175 {
6176   int x, y;
6177
6178   SCAN_PLAYFIELD(x, y)
6179   {
6180     int element = Feld[x][y];
6181
6182     if (element == EL_EMC_FAKE_GRASS &&
6183         game.magnify_time_left > 0)
6184     {
6185       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6186       TEST_DrawLevelField(x, y);
6187     }
6188     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6189              game.magnify_time_left == 0)
6190     {
6191       Feld[x][y] = EL_EMC_FAKE_GRASS;
6192       TEST_DrawLevelField(x, y);
6193     }
6194     else if (IS_GATE_GRAY(element) &&
6195              game.magnify_time_left > 0)
6196     {
6197       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6198                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6199                     IS_EM_GATE_GRAY(element) ?
6200                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6201                     IS_EMC_GATE_GRAY(element) ?
6202                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6203                     IS_DC_GATE_GRAY(element) ?
6204                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6205                     element);
6206       TEST_DrawLevelField(x, y);
6207     }
6208     else if (IS_GATE_GRAY_ACTIVE(element) &&
6209              game.magnify_time_left == 0)
6210     {
6211       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6212                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6213                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6214                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6215                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6216                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6217                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6218                     EL_DC_GATE_WHITE_GRAY :
6219                     element);
6220       TEST_DrawLevelField(x, y);
6221     }
6222   }
6223 }
6224
6225 static void ToggleLightSwitch(int x, int y)
6226 {
6227   int element = Feld[x][y];
6228
6229   game.light_time_left =
6230     (element == EL_LIGHT_SWITCH ?
6231      level.time_light * FRAMES_PER_SECOND : 0);
6232
6233   RedrawAllLightSwitchesAndInvisibleElements();
6234 }
6235
6236 static void ActivateTimegateSwitch(int x, int y)
6237 {
6238   int xx, yy;
6239
6240   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6241
6242   SCAN_PLAYFIELD(xx, yy)
6243   {
6244     int element = Feld[xx][yy];
6245
6246     if (element == EL_TIMEGATE_CLOSED ||
6247         element == EL_TIMEGATE_CLOSING)
6248     {
6249       Feld[xx][yy] = EL_TIMEGATE_OPENING;
6250       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6251     }
6252
6253     /*
6254     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6255     {
6256       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6257       TEST_DrawLevelField(xx, yy);
6258     }
6259     */
6260
6261   }
6262
6263   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6264                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6265 }
6266
6267 static void Impact(int x, int y)
6268 {
6269   boolean last_line = (y == lev_fieldy - 1);
6270   boolean object_hit = FALSE;
6271   boolean impact = (last_line || object_hit);
6272   int element = Feld[x][y];
6273   int smashed = EL_STEELWALL;
6274
6275   if (!last_line)       // check if element below was hit
6276   {
6277     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6278       return;
6279
6280     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6281                                          MovDir[x][y + 1] != MV_DOWN ||
6282                                          MovPos[x][y + 1] <= TILEY / 2));
6283
6284     // do not smash moving elements that left the smashed field in time
6285     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6286         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6287       object_hit = FALSE;
6288
6289 #if USE_QUICKSAND_IMPACT_BUGFIX
6290     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6291     {
6292       RemoveMovingField(x, y + 1);
6293       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6294       Feld[x][y + 2] = EL_ROCK;
6295       TEST_DrawLevelField(x, y + 2);
6296
6297       object_hit = TRUE;
6298     }
6299
6300     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6301     {
6302       RemoveMovingField(x, y + 1);
6303       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6304       Feld[x][y + 2] = EL_ROCK;
6305       TEST_DrawLevelField(x, y + 2);
6306
6307       object_hit = TRUE;
6308     }
6309 #endif
6310
6311     if (object_hit)
6312       smashed = MovingOrBlocked2Element(x, y + 1);
6313
6314     impact = (last_line || object_hit);
6315   }
6316
6317   if (!last_line && smashed == EL_ACID) // element falls into acid
6318   {
6319     SplashAcid(x, y + 1);
6320     return;
6321   }
6322
6323   // !!! not sufficient for all cases -- see EL_PEARL below !!!
6324   // only reset graphic animation if graphic really changes after impact
6325   if (impact &&
6326       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6327   {
6328     ResetGfxAnimation(x, y);
6329     TEST_DrawLevelField(x, y);
6330   }
6331
6332   if (impact && CAN_EXPLODE_IMPACT(element))
6333   {
6334     Bang(x, y);
6335     return;
6336   }
6337   else if (impact && element == EL_PEARL &&
6338            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6339   {
6340     ResetGfxAnimation(x, y);
6341
6342     Feld[x][y] = EL_PEARL_BREAKING;
6343     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6344     return;
6345   }
6346   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6347   {
6348     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6349
6350     return;
6351   }
6352
6353   if (impact && element == EL_AMOEBA_DROP)
6354   {
6355     if (object_hit && IS_PLAYER(x, y + 1))
6356       KillPlayerUnlessEnemyProtected(x, y + 1);
6357     else if (object_hit && smashed == EL_PENGUIN)
6358       Bang(x, y + 1);
6359     else
6360     {
6361       Feld[x][y] = EL_AMOEBA_GROWING;
6362       Store[x][y] = EL_AMOEBA_WET;
6363
6364       ResetRandomAnimationValue(x, y);
6365     }
6366     return;
6367   }
6368
6369   if (object_hit)               // check which object was hit
6370   {
6371     if ((CAN_PASS_MAGIC_WALL(element) && 
6372          (smashed == EL_MAGIC_WALL ||
6373           smashed == EL_BD_MAGIC_WALL)) ||
6374         (CAN_PASS_DC_MAGIC_WALL(element) &&
6375          smashed == EL_DC_MAGIC_WALL))
6376     {
6377       int xx, yy;
6378       int activated_magic_wall =
6379         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6380          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6381          EL_DC_MAGIC_WALL_ACTIVE);
6382
6383       // activate magic wall / mill
6384       SCAN_PLAYFIELD(xx, yy)
6385       {
6386         if (Feld[xx][yy] == smashed)
6387           Feld[xx][yy] = activated_magic_wall;
6388       }
6389
6390       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6391       game.magic_wall_active = TRUE;
6392
6393       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6394                             SND_MAGIC_WALL_ACTIVATING :
6395                             smashed == EL_BD_MAGIC_WALL ?
6396                             SND_BD_MAGIC_WALL_ACTIVATING :
6397                             SND_DC_MAGIC_WALL_ACTIVATING));
6398     }
6399
6400     if (IS_PLAYER(x, y + 1))
6401     {
6402       if (CAN_SMASH_PLAYER(element))
6403       {
6404         KillPlayerUnlessEnemyProtected(x, y + 1);
6405         return;
6406       }
6407     }
6408     else if (smashed == EL_PENGUIN)
6409     {
6410       if (CAN_SMASH_PLAYER(element))
6411       {
6412         Bang(x, y + 1);
6413         return;
6414       }
6415     }
6416     else if (element == EL_BD_DIAMOND)
6417     {
6418       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6419       {
6420         Bang(x, y + 1);
6421         return;
6422       }
6423     }
6424     else if (((element == EL_SP_INFOTRON ||
6425                element == EL_SP_ZONK) &&
6426               (smashed == EL_SP_SNIKSNAK ||
6427                smashed == EL_SP_ELECTRON ||
6428                smashed == EL_SP_DISK_ORANGE)) ||
6429              (element == EL_SP_INFOTRON &&
6430               smashed == EL_SP_DISK_YELLOW))
6431     {
6432       Bang(x, y + 1);
6433       return;
6434     }
6435     else if (CAN_SMASH_EVERYTHING(element))
6436     {
6437       if (IS_CLASSIC_ENEMY(smashed) ||
6438           CAN_EXPLODE_SMASHED(smashed))
6439       {
6440         Bang(x, y + 1);
6441         return;
6442       }
6443       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6444       {
6445         if (smashed == EL_LAMP ||
6446             smashed == EL_LAMP_ACTIVE)
6447         {
6448           Bang(x, y + 1);
6449           return;
6450         }
6451         else if (smashed == EL_NUT)
6452         {
6453           Feld[x][y + 1] = EL_NUT_BREAKING;
6454           PlayLevelSound(x, y, SND_NUT_BREAKING);
6455           RaiseScoreElement(EL_NUT);
6456           return;
6457         }
6458         else if (smashed == EL_PEARL)
6459         {
6460           ResetGfxAnimation(x, y);
6461
6462           Feld[x][y + 1] = EL_PEARL_BREAKING;
6463           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6464           return;
6465         }
6466         else if (smashed == EL_DIAMOND)
6467         {
6468           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6469           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6470           return;
6471         }
6472         else if (IS_BELT_SWITCH(smashed))
6473         {
6474           ToggleBeltSwitch(x, y + 1);
6475         }
6476         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6477                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6478                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6479                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6480         {
6481           ToggleSwitchgateSwitch(x, y + 1);
6482         }
6483         else if (smashed == EL_LIGHT_SWITCH ||
6484                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6485         {
6486           ToggleLightSwitch(x, y + 1);
6487         }
6488         else
6489         {
6490           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6491
6492           CheckElementChangeBySide(x, y + 1, smashed, element,
6493                                    CE_SWITCHED, CH_SIDE_TOP);
6494           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6495                                             CH_SIDE_TOP);
6496         }
6497       }
6498       else
6499       {
6500         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6501       }
6502     }
6503   }
6504
6505   // play sound of magic wall / mill
6506   if (!last_line &&
6507       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6508        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6509        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6510   {
6511     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6512       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6513     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6514       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6515     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6516       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6517
6518     return;
6519   }
6520
6521   // play sound of object that hits the ground
6522   if (last_line || object_hit)
6523     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6524 }
6525
6526 static void TurnRoundExt(int x, int y)
6527 {
6528   static struct
6529   {
6530     int dx, dy;
6531   } move_xy[] =
6532   {
6533     {  0,  0 },
6534     { -1,  0 },
6535     { +1,  0 },
6536     {  0,  0 },
6537     {  0, -1 },
6538     {  0,  0 }, { 0, 0 }, { 0, 0 },
6539     {  0, +1 }
6540   };
6541   static struct
6542   {
6543     int left, right, back;
6544   } turn[] =
6545   {
6546     { 0,        0,              0        },
6547     { MV_DOWN,  MV_UP,          MV_RIGHT },
6548     { MV_UP,    MV_DOWN,        MV_LEFT  },
6549     { 0,        0,              0        },
6550     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6551     { 0,        0,              0        },
6552     { 0,        0,              0        },
6553     { 0,        0,              0        },
6554     { MV_RIGHT, MV_LEFT,        MV_UP    }
6555   };
6556
6557   int element = Feld[x][y];
6558   int move_pattern = element_info[element].move_pattern;
6559
6560   int old_move_dir = MovDir[x][y];
6561   int left_dir  = turn[old_move_dir].left;
6562   int right_dir = turn[old_move_dir].right;
6563   int back_dir  = turn[old_move_dir].back;
6564
6565   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6566   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6567   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6568   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6569
6570   int left_x  = x + left_dx,  left_y  = y + left_dy;
6571   int right_x = x + right_dx, right_y = y + right_dy;
6572   int move_x  = x + move_dx,  move_y  = y + move_dy;
6573
6574   int xx, yy;
6575
6576   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6577   {
6578     TestIfBadThingTouchesOtherBadThing(x, y);
6579
6580     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6581       MovDir[x][y] = right_dir;
6582     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6583       MovDir[x][y] = left_dir;
6584
6585     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6586       MovDelay[x][y] = 9;
6587     else if (element == EL_BD_BUTTERFLY)     // && MovDir[x][y] == left_dir)
6588       MovDelay[x][y] = 1;
6589   }
6590   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6591   {
6592     TestIfBadThingTouchesOtherBadThing(x, y);
6593
6594     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6595       MovDir[x][y] = left_dir;
6596     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6597       MovDir[x][y] = right_dir;
6598
6599     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6600       MovDelay[x][y] = 9;
6601     else if (element == EL_BD_FIREFLY)      // && MovDir[x][y] == right_dir)
6602       MovDelay[x][y] = 1;
6603   }
6604   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6605   {
6606     TestIfBadThingTouchesOtherBadThing(x, y);
6607
6608     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6609       MovDir[x][y] = left_dir;
6610     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6611       MovDir[x][y] = right_dir;
6612
6613     if (MovDir[x][y] != old_move_dir)
6614       MovDelay[x][y] = 9;
6615   }
6616   else if (element == EL_YAMYAM)
6617   {
6618     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6619     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6620
6621     if (can_turn_left && can_turn_right)
6622       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6623     else if (can_turn_left)
6624       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6625     else if (can_turn_right)
6626       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6627     else
6628       MovDir[x][y] = back_dir;
6629
6630     MovDelay[x][y] = 16 + 16 * RND(3);
6631   }
6632   else if (element == EL_DARK_YAMYAM)
6633   {
6634     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6635                                                          left_x, left_y);
6636     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6637                                                          right_x, right_y);
6638
6639     if (can_turn_left && can_turn_right)
6640       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6641     else if (can_turn_left)
6642       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6643     else if (can_turn_right)
6644       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6645     else
6646       MovDir[x][y] = back_dir;
6647
6648     MovDelay[x][y] = 16 + 16 * RND(3);
6649   }
6650   else if (element == EL_PACMAN)
6651   {
6652     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6653     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6654
6655     if (can_turn_left && can_turn_right)
6656       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6657     else if (can_turn_left)
6658       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6659     else if (can_turn_right)
6660       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6661     else
6662       MovDir[x][y] = back_dir;
6663
6664     MovDelay[x][y] = 6 + RND(40);
6665   }
6666   else if (element == EL_PIG)
6667   {
6668     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6669     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6670     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6671     boolean should_turn_left, should_turn_right, should_move_on;
6672     int rnd_value = 24;
6673     int rnd = RND(rnd_value);
6674
6675     should_turn_left = (can_turn_left &&
6676                         (!can_move_on ||
6677                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6678                                                    y + back_dy + left_dy)));
6679     should_turn_right = (can_turn_right &&
6680                          (!can_move_on ||
6681                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6682                                                     y + back_dy + right_dy)));
6683     should_move_on = (can_move_on &&
6684                       (!can_turn_left ||
6685                        !can_turn_right ||
6686                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6687                                                  y + move_dy + left_dy) ||
6688                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6689                                                  y + move_dy + right_dy)));
6690
6691     if (should_turn_left || should_turn_right || should_move_on)
6692     {
6693       if (should_turn_left && should_turn_right && should_move_on)
6694         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6695                         rnd < 2 * rnd_value / 3 ? right_dir :
6696                         old_move_dir);
6697       else if (should_turn_left && should_turn_right)
6698         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6699       else if (should_turn_left && should_move_on)
6700         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6701       else if (should_turn_right && should_move_on)
6702         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6703       else if (should_turn_left)
6704         MovDir[x][y] = left_dir;
6705       else if (should_turn_right)
6706         MovDir[x][y] = right_dir;
6707       else if (should_move_on)
6708         MovDir[x][y] = old_move_dir;
6709     }
6710     else if (can_move_on && rnd > rnd_value / 8)
6711       MovDir[x][y] = old_move_dir;
6712     else if (can_turn_left && can_turn_right)
6713       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6714     else if (can_turn_left && rnd > rnd_value / 8)
6715       MovDir[x][y] = left_dir;
6716     else if (can_turn_right && rnd > rnd_value/8)
6717       MovDir[x][y] = right_dir;
6718     else
6719       MovDir[x][y] = back_dir;
6720
6721     xx = x + move_xy[MovDir[x][y]].dx;
6722     yy = y + move_xy[MovDir[x][y]].dy;
6723
6724     if (!IN_LEV_FIELD(xx, yy) ||
6725         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6726       MovDir[x][y] = old_move_dir;
6727
6728     MovDelay[x][y] = 0;
6729   }
6730   else if (element == EL_DRAGON)
6731   {
6732     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6733     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6734     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6735     int rnd_value = 24;
6736     int rnd = RND(rnd_value);
6737
6738     if (can_move_on && rnd > rnd_value / 8)
6739       MovDir[x][y] = old_move_dir;
6740     else if (can_turn_left && can_turn_right)
6741       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6742     else if (can_turn_left && rnd > rnd_value / 8)
6743       MovDir[x][y] = left_dir;
6744     else if (can_turn_right && rnd > rnd_value / 8)
6745       MovDir[x][y] = right_dir;
6746     else
6747       MovDir[x][y] = back_dir;
6748
6749     xx = x + move_xy[MovDir[x][y]].dx;
6750     yy = y + move_xy[MovDir[x][y]].dy;
6751
6752     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6753       MovDir[x][y] = old_move_dir;
6754
6755     MovDelay[x][y] = 0;
6756   }
6757   else if (element == EL_MOLE)
6758   {
6759     boolean can_move_on =
6760       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6761                             IS_AMOEBOID(Feld[move_x][move_y]) ||
6762                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6763     if (!can_move_on)
6764     {
6765       boolean can_turn_left =
6766         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6767                               IS_AMOEBOID(Feld[left_x][left_y])));
6768
6769       boolean can_turn_right =
6770         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6771                               IS_AMOEBOID(Feld[right_x][right_y])));
6772
6773       if (can_turn_left && can_turn_right)
6774         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6775       else if (can_turn_left)
6776         MovDir[x][y] = left_dir;
6777       else
6778         MovDir[x][y] = right_dir;
6779     }
6780
6781     if (MovDir[x][y] != old_move_dir)
6782       MovDelay[x][y] = 9;
6783   }
6784   else if (element == EL_BALLOON)
6785   {
6786     MovDir[x][y] = game.wind_direction;
6787     MovDelay[x][y] = 0;
6788   }
6789   else if (element == EL_SPRING)
6790   {
6791     if (MovDir[x][y] & MV_HORIZONTAL)
6792     {
6793       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6794           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6795       {
6796         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6797         ResetGfxAnimation(move_x, move_y);
6798         TEST_DrawLevelField(move_x, move_y);
6799
6800         MovDir[x][y] = back_dir;
6801       }
6802       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6803                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6804         MovDir[x][y] = MV_NONE;
6805     }
6806
6807     MovDelay[x][y] = 0;
6808   }
6809   else if (element == EL_ROBOT ||
6810            element == EL_SATELLITE ||
6811            element == EL_PENGUIN ||
6812            element == EL_EMC_ANDROID)
6813   {
6814     int attr_x = -1, attr_y = -1;
6815
6816     if (AllPlayersGone)
6817     {
6818       attr_x = ExitX;
6819       attr_y = ExitY;
6820     }
6821     else
6822     {
6823       int i;
6824
6825       for (i = 0; i < MAX_PLAYERS; i++)
6826       {
6827         struct PlayerInfo *player = &stored_player[i];
6828         int jx = player->jx, jy = player->jy;
6829
6830         if (!player->active)
6831           continue;
6832
6833         if (attr_x == -1 ||
6834             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6835         {
6836           attr_x = jx;
6837           attr_y = jy;
6838         }
6839       }
6840     }
6841
6842     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6843         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6844          game.engine_version < VERSION_IDENT(3,1,0,0)))
6845     {
6846       attr_x = ZX;
6847       attr_y = ZY;
6848     }
6849
6850     if (element == EL_PENGUIN)
6851     {
6852       int i;
6853       static int xy[4][2] =
6854       {
6855         { 0, -1 },
6856         { -1, 0 },
6857         { +1, 0 },
6858         { 0, +1 }
6859       };
6860
6861       for (i = 0; i < NUM_DIRECTIONS; i++)
6862       {
6863         int ex = x + xy[i][0];
6864         int ey = y + xy[i][1];
6865
6866         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6867                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6868                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6869                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6870         {
6871           attr_x = ex;
6872           attr_y = ey;
6873           break;
6874         }
6875       }
6876     }
6877
6878     MovDir[x][y] = MV_NONE;
6879     if (attr_x < x)
6880       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
6881     else if (attr_x > x)
6882       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
6883     if (attr_y < y)
6884       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
6885     else if (attr_y > y)
6886       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
6887
6888     if (element == EL_ROBOT)
6889     {
6890       int newx, newy;
6891
6892       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6893         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6894       Moving2Blocked(x, y, &newx, &newy);
6895
6896       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6897         MovDelay[x][y] = 8 + 8 * !RND(3);
6898       else
6899         MovDelay[x][y] = 16;
6900     }
6901     else if (element == EL_PENGUIN)
6902     {
6903       int newx, newy;
6904
6905       MovDelay[x][y] = 1;
6906
6907       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6908       {
6909         boolean first_horiz = RND(2);
6910         int new_move_dir = MovDir[x][y];
6911
6912         MovDir[x][y] =
6913           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6914         Moving2Blocked(x, y, &newx, &newy);
6915
6916         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6917           return;
6918
6919         MovDir[x][y] =
6920           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6921         Moving2Blocked(x, y, &newx, &newy);
6922
6923         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6924           return;
6925
6926         MovDir[x][y] = old_move_dir;
6927         return;
6928       }
6929     }
6930     else if (element == EL_SATELLITE)
6931     {
6932       int newx, newy;
6933
6934       MovDelay[x][y] = 1;
6935
6936       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6937       {
6938         boolean first_horiz = RND(2);
6939         int new_move_dir = MovDir[x][y];
6940
6941         MovDir[x][y] =
6942           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6943         Moving2Blocked(x, y, &newx, &newy);
6944
6945         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6946           return;
6947
6948         MovDir[x][y] =
6949           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6950         Moving2Blocked(x, y, &newx, &newy);
6951
6952         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6953           return;
6954
6955         MovDir[x][y] = old_move_dir;
6956         return;
6957       }
6958     }
6959     else if (element == EL_EMC_ANDROID)
6960     {
6961       static int check_pos[16] =
6962       {
6963         -1,             //  0 => (invalid)
6964         7,              //  1 => MV_LEFT
6965         3,              //  2 => MV_RIGHT
6966         -1,             //  3 => (invalid)
6967         1,              //  4 =>            MV_UP
6968         0,              //  5 => MV_LEFT  | MV_UP
6969         2,              //  6 => MV_RIGHT | MV_UP
6970         -1,             //  7 => (invalid)
6971         5,              //  8 =>            MV_DOWN
6972         6,              //  9 => MV_LEFT  | MV_DOWN
6973         4,              // 10 => MV_RIGHT | MV_DOWN
6974         -1,             // 11 => (invalid)
6975         -1,             // 12 => (invalid)
6976         -1,             // 13 => (invalid)
6977         -1,             // 14 => (invalid)
6978         -1,             // 15 => (invalid)
6979       };
6980       static struct
6981       {
6982         int dx, dy;
6983         int dir;
6984       } check_xy[8] =
6985       {
6986         { -1, -1,       MV_LEFT  | MV_UP   },
6987         {  0, -1,                  MV_UP   },
6988         { +1, -1,       MV_RIGHT | MV_UP   },
6989         { +1,  0,       MV_RIGHT           },
6990         { +1, +1,       MV_RIGHT | MV_DOWN },
6991         {  0, +1,                  MV_DOWN },
6992         { -1, +1,       MV_LEFT  | MV_DOWN },
6993         { -1,  0,       MV_LEFT            },
6994       };
6995       int start_pos, check_order;
6996       boolean can_clone = FALSE;
6997       int i;
6998
6999       // check if there is any free field around current position
7000       for (i = 0; i < 8; i++)
7001       {
7002         int newx = x + check_xy[i].dx;
7003         int newy = y + check_xy[i].dy;
7004
7005         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7006         {
7007           can_clone = TRUE;
7008
7009           break;
7010         }
7011       }
7012
7013       if (can_clone)            // randomly find an element to clone
7014       {
7015         can_clone = FALSE;
7016
7017         start_pos = check_pos[RND(8)];
7018         check_order = (RND(2) ? -1 : +1);
7019
7020         for (i = 0; i < 8; i++)
7021         {
7022           int pos_raw = start_pos + i * check_order;
7023           int pos = (pos_raw + 8) % 8;
7024           int newx = x + check_xy[pos].dx;
7025           int newy = y + check_xy[pos].dy;
7026
7027           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7028           {
7029             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7030             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7031
7032             Store[x][y] = Feld[newx][newy];
7033
7034             can_clone = TRUE;
7035
7036             break;
7037           }
7038         }
7039       }
7040
7041       if (can_clone)            // randomly find a direction to move
7042       {
7043         can_clone = FALSE;
7044
7045         start_pos = check_pos[RND(8)];
7046         check_order = (RND(2) ? -1 : +1);
7047
7048         for (i = 0; i < 8; i++)
7049         {
7050           int pos_raw = start_pos + i * check_order;
7051           int pos = (pos_raw + 8) % 8;
7052           int newx = x + check_xy[pos].dx;
7053           int newy = y + check_xy[pos].dy;
7054           int new_move_dir = check_xy[pos].dir;
7055
7056           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7057           {
7058             MovDir[x][y] = new_move_dir;
7059             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7060
7061             can_clone = TRUE;
7062
7063             break;
7064           }
7065         }
7066       }
7067
7068       if (can_clone)            // cloning and moving successful
7069         return;
7070
7071       // cannot clone -- try to move towards player
7072
7073       start_pos = check_pos[MovDir[x][y] & 0x0f];
7074       check_order = (RND(2) ? -1 : +1);
7075
7076       for (i = 0; i < 3; i++)
7077       {
7078         // first check start_pos, then previous/next or (next/previous) pos
7079         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7080         int pos = (pos_raw + 8) % 8;
7081         int newx = x + check_xy[pos].dx;
7082         int newy = y + check_xy[pos].dy;
7083         int new_move_dir = check_xy[pos].dir;
7084
7085         if (IS_PLAYER(newx, newy))
7086           break;
7087
7088         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7089         {
7090           MovDir[x][y] = new_move_dir;
7091           MovDelay[x][y] = level.android_move_time * 8 + 1;
7092
7093           break;
7094         }
7095       }
7096     }
7097   }
7098   else if (move_pattern == MV_TURNING_LEFT ||
7099            move_pattern == MV_TURNING_RIGHT ||
7100            move_pattern == MV_TURNING_LEFT_RIGHT ||
7101            move_pattern == MV_TURNING_RIGHT_LEFT ||
7102            move_pattern == MV_TURNING_RANDOM ||
7103            move_pattern == MV_ALL_DIRECTIONS)
7104   {
7105     boolean can_turn_left =
7106       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7107     boolean can_turn_right =
7108       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7109
7110     if (element_info[element].move_stepsize == 0)       // "not moving"
7111       return;
7112
7113     if (move_pattern == MV_TURNING_LEFT)
7114       MovDir[x][y] = left_dir;
7115     else if (move_pattern == MV_TURNING_RIGHT)
7116       MovDir[x][y] = right_dir;
7117     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7118       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7119     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7120       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7121     else if (move_pattern == MV_TURNING_RANDOM)
7122       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7123                       can_turn_right && !can_turn_left ? right_dir :
7124                       RND(2) ? left_dir : right_dir);
7125     else if (can_turn_left && can_turn_right)
7126       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7127     else if (can_turn_left)
7128       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7129     else if (can_turn_right)
7130       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7131     else
7132       MovDir[x][y] = back_dir;
7133
7134     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7135   }
7136   else if (move_pattern == MV_HORIZONTAL ||
7137            move_pattern == MV_VERTICAL)
7138   {
7139     if (move_pattern & old_move_dir)
7140       MovDir[x][y] = back_dir;
7141     else if (move_pattern == MV_HORIZONTAL)
7142       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7143     else if (move_pattern == MV_VERTICAL)
7144       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7145
7146     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7147   }
7148   else if (move_pattern & MV_ANY_DIRECTION)
7149   {
7150     MovDir[x][y] = move_pattern;
7151     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7152   }
7153   else if (move_pattern & MV_WIND_DIRECTION)
7154   {
7155     MovDir[x][y] = game.wind_direction;
7156     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7157   }
7158   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7159   {
7160     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7161       MovDir[x][y] = left_dir;
7162     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7163       MovDir[x][y] = right_dir;
7164
7165     if (MovDir[x][y] != old_move_dir)
7166       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7167   }
7168   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7169   {
7170     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7171       MovDir[x][y] = right_dir;
7172     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7173       MovDir[x][y] = left_dir;
7174
7175     if (MovDir[x][y] != old_move_dir)
7176       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7177   }
7178   else if (move_pattern == MV_TOWARDS_PLAYER ||
7179            move_pattern == MV_AWAY_FROM_PLAYER)
7180   {
7181     int attr_x = -1, attr_y = -1;
7182     int newx, newy;
7183     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7184
7185     if (AllPlayersGone)
7186     {
7187       attr_x = ExitX;
7188       attr_y = ExitY;
7189     }
7190     else
7191     {
7192       int i;
7193
7194       for (i = 0; i < MAX_PLAYERS; i++)
7195       {
7196         struct PlayerInfo *player = &stored_player[i];
7197         int jx = player->jx, jy = player->jy;
7198
7199         if (!player->active)
7200           continue;
7201
7202         if (attr_x == -1 ||
7203             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7204         {
7205           attr_x = jx;
7206           attr_y = jy;
7207         }
7208       }
7209     }
7210
7211     MovDir[x][y] = MV_NONE;
7212     if (attr_x < x)
7213       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7214     else if (attr_x > x)
7215       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7216     if (attr_y < y)
7217       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7218     else if (attr_y > y)
7219       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7220
7221     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7222
7223     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7224     {
7225       boolean first_horiz = RND(2);
7226       int new_move_dir = MovDir[x][y];
7227
7228       if (element_info[element].move_stepsize == 0)     // "not moving"
7229       {
7230         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7231         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7232
7233         return;
7234       }
7235
7236       MovDir[x][y] =
7237         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7238       Moving2Blocked(x, y, &newx, &newy);
7239
7240       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7241         return;
7242
7243       MovDir[x][y] =
7244         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7245       Moving2Blocked(x, y, &newx, &newy);
7246
7247       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7248         return;
7249
7250       MovDir[x][y] = old_move_dir;
7251     }
7252   }
7253   else if (move_pattern == MV_WHEN_PUSHED ||
7254            move_pattern == MV_WHEN_DROPPED)
7255   {
7256     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7257       MovDir[x][y] = MV_NONE;
7258
7259     MovDelay[x][y] = 0;
7260   }
7261   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7262   {
7263     static int test_xy[7][2] =
7264     {
7265       { 0, -1 },
7266       { -1, 0 },
7267       { +1, 0 },
7268       { 0, +1 },
7269       { 0, -1 },
7270       { -1, 0 },
7271       { +1, 0 },
7272     };
7273     static int test_dir[7] =
7274     {
7275       MV_UP,
7276       MV_LEFT,
7277       MV_RIGHT,
7278       MV_DOWN,
7279       MV_UP,
7280       MV_LEFT,
7281       MV_RIGHT,
7282     };
7283     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7284     int move_preference = -1000000;     // start with very low preference
7285     int new_move_dir = MV_NONE;
7286     int start_test = RND(4);
7287     int i;
7288
7289     for (i = 0; i < NUM_DIRECTIONS; i++)
7290     {
7291       int move_dir = test_dir[start_test + i];
7292       int move_dir_preference;
7293
7294       xx = x + test_xy[start_test + i][0];
7295       yy = y + test_xy[start_test + i][1];
7296
7297       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7298           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7299       {
7300         new_move_dir = move_dir;
7301
7302         break;
7303       }
7304
7305       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7306         continue;
7307
7308       move_dir_preference = -1 * RunnerVisit[xx][yy];
7309       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7310         move_dir_preference = PlayerVisit[xx][yy];
7311
7312       if (move_dir_preference > move_preference)
7313       {
7314         // prefer field that has not been visited for the longest time
7315         move_preference = move_dir_preference;
7316         new_move_dir = move_dir;
7317       }
7318       else if (move_dir_preference == move_preference &&
7319                move_dir == old_move_dir)
7320       {
7321         // prefer last direction when all directions are preferred equally
7322         move_preference = move_dir_preference;
7323         new_move_dir = move_dir;
7324       }
7325     }
7326
7327     MovDir[x][y] = new_move_dir;
7328     if (old_move_dir != new_move_dir)
7329       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7330   }
7331 }
7332
7333 static void TurnRound(int x, int y)
7334 {
7335   int direction = MovDir[x][y];
7336
7337   TurnRoundExt(x, y);
7338
7339   GfxDir[x][y] = MovDir[x][y];
7340
7341   if (direction != MovDir[x][y])
7342     GfxFrame[x][y] = 0;
7343
7344   if (MovDelay[x][y])
7345     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7346
7347   ResetGfxFrame(x, y);
7348 }
7349
7350 static boolean JustBeingPushed(int x, int y)
7351 {
7352   int i;
7353
7354   for (i = 0; i < MAX_PLAYERS; i++)
7355   {
7356     struct PlayerInfo *player = &stored_player[i];
7357
7358     if (player->active && player->is_pushing && player->MovPos)
7359     {
7360       int next_jx = player->jx + (player->jx - player->last_jx);
7361       int next_jy = player->jy + (player->jy - player->last_jy);
7362
7363       if (x == next_jx && y == next_jy)
7364         return TRUE;
7365     }
7366   }
7367
7368   return FALSE;
7369 }
7370
7371 static void StartMoving(int x, int y)
7372 {
7373   boolean started_moving = FALSE;       // some elements can fall _and_ move
7374   int element = Feld[x][y];
7375
7376   if (Stop[x][y])
7377     return;
7378
7379   if (MovDelay[x][y] == 0)
7380     GfxAction[x][y] = ACTION_DEFAULT;
7381
7382   if (CAN_FALL(element) && y < lev_fieldy - 1)
7383   {
7384     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7385         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7386       if (JustBeingPushed(x, y))
7387         return;
7388
7389     if (element == EL_QUICKSAND_FULL)
7390     {
7391       if (IS_FREE(x, y + 1))
7392       {
7393         InitMovingField(x, y, MV_DOWN);
7394         started_moving = TRUE;
7395
7396         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7397 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7398         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7399           Store[x][y] = EL_ROCK;
7400 #else
7401         Store[x][y] = EL_ROCK;
7402 #endif
7403
7404         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7405       }
7406       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7407       {
7408         if (!MovDelay[x][y])
7409         {
7410           MovDelay[x][y] = TILEY + 1;
7411
7412           ResetGfxAnimation(x, y);
7413           ResetGfxAnimation(x, y + 1);
7414         }
7415
7416         if (MovDelay[x][y])
7417         {
7418           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7419           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7420
7421           MovDelay[x][y]--;
7422           if (MovDelay[x][y])
7423             return;
7424         }
7425
7426         Feld[x][y] = EL_QUICKSAND_EMPTY;
7427         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7428         Store[x][y + 1] = Store[x][y];
7429         Store[x][y] = 0;
7430
7431         PlayLevelSoundAction(x, y, ACTION_FILLING);
7432       }
7433       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7434       {
7435         if (!MovDelay[x][y])
7436         {
7437           MovDelay[x][y] = TILEY + 1;
7438
7439           ResetGfxAnimation(x, y);
7440           ResetGfxAnimation(x, y + 1);
7441         }
7442
7443         if (MovDelay[x][y])
7444         {
7445           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7446           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7447
7448           MovDelay[x][y]--;
7449           if (MovDelay[x][y])
7450             return;
7451         }
7452
7453         Feld[x][y] = EL_QUICKSAND_EMPTY;
7454         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7455         Store[x][y + 1] = Store[x][y];
7456         Store[x][y] = 0;
7457
7458         PlayLevelSoundAction(x, y, ACTION_FILLING);
7459       }
7460     }
7461     else if (element == EL_QUICKSAND_FAST_FULL)
7462     {
7463       if (IS_FREE(x, y + 1))
7464       {
7465         InitMovingField(x, y, MV_DOWN);
7466         started_moving = TRUE;
7467
7468         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7469 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7470         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7471           Store[x][y] = EL_ROCK;
7472 #else
7473         Store[x][y] = EL_ROCK;
7474 #endif
7475
7476         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7477       }
7478       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7479       {
7480         if (!MovDelay[x][y])
7481         {
7482           MovDelay[x][y] = TILEY + 1;
7483
7484           ResetGfxAnimation(x, y);
7485           ResetGfxAnimation(x, y + 1);
7486         }
7487
7488         if (MovDelay[x][y])
7489         {
7490           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7491           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7492
7493           MovDelay[x][y]--;
7494           if (MovDelay[x][y])
7495             return;
7496         }
7497
7498         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7499         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7500         Store[x][y + 1] = Store[x][y];
7501         Store[x][y] = 0;
7502
7503         PlayLevelSoundAction(x, y, ACTION_FILLING);
7504       }
7505       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7506       {
7507         if (!MovDelay[x][y])
7508         {
7509           MovDelay[x][y] = TILEY + 1;
7510
7511           ResetGfxAnimation(x, y);
7512           ResetGfxAnimation(x, y + 1);
7513         }
7514
7515         if (MovDelay[x][y])
7516         {
7517           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7518           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7519
7520           MovDelay[x][y]--;
7521           if (MovDelay[x][y])
7522             return;
7523         }
7524
7525         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7526         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7527         Store[x][y + 1] = Store[x][y];
7528         Store[x][y] = 0;
7529
7530         PlayLevelSoundAction(x, y, ACTION_FILLING);
7531       }
7532     }
7533     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7534              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7535     {
7536       InitMovingField(x, y, MV_DOWN);
7537       started_moving = TRUE;
7538
7539       Feld[x][y] = EL_QUICKSAND_FILLING;
7540       Store[x][y] = element;
7541
7542       PlayLevelSoundAction(x, y, ACTION_FILLING);
7543     }
7544     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7545              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7546     {
7547       InitMovingField(x, y, MV_DOWN);
7548       started_moving = TRUE;
7549
7550       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7551       Store[x][y] = element;
7552
7553       PlayLevelSoundAction(x, y, ACTION_FILLING);
7554     }
7555     else if (element == EL_MAGIC_WALL_FULL)
7556     {
7557       if (IS_FREE(x, y + 1))
7558       {
7559         InitMovingField(x, y, MV_DOWN);
7560         started_moving = TRUE;
7561
7562         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7563         Store[x][y] = EL_CHANGED(Store[x][y]);
7564       }
7565       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7566       {
7567         if (!MovDelay[x][y])
7568           MovDelay[x][y] = TILEY / 4 + 1;
7569
7570         if (MovDelay[x][y])
7571         {
7572           MovDelay[x][y]--;
7573           if (MovDelay[x][y])
7574             return;
7575         }
7576
7577         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7578         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7579         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7580         Store[x][y] = 0;
7581       }
7582     }
7583     else if (element == EL_BD_MAGIC_WALL_FULL)
7584     {
7585       if (IS_FREE(x, y + 1))
7586       {
7587         InitMovingField(x, y, MV_DOWN);
7588         started_moving = TRUE;
7589
7590         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7591         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7592       }
7593       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7594       {
7595         if (!MovDelay[x][y])
7596           MovDelay[x][y] = TILEY / 4 + 1;
7597
7598         if (MovDelay[x][y])
7599         {
7600           MovDelay[x][y]--;
7601           if (MovDelay[x][y])
7602             return;
7603         }
7604
7605         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7606         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7607         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7608         Store[x][y] = 0;
7609       }
7610     }
7611     else if (element == EL_DC_MAGIC_WALL_FULL)
7612     {
7613       if (IS_FREE(x, y + 1))
7614       {
7615         InitMovingField(x, y, MV_DOWN);
7616         started_moving = TRUE;
7617
7618         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7619         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7620       }
7621       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7622       {
7623         if (!MovDelay[x][y])
7624           MovDelay[x][y] = TILEY / 4 + 1;
7625
7626         if (MovDelay[x][y])
7627         {
7628           MovDelay[x][y]--;
7629           if (MovDelay[x][y])
7630             return;
7631         }
7632
7633         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7634         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7635         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7636         Store[x][y] = 0;
7637       }
7638     }
7639     else if ((CAN_PASS_MAGIC_WALL(element) &&
7640               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7641                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7642              (CAN_PASS_DC_MAGIC_WALL(element) &&
7643               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7644
7645     {
7646       InitMovingField(x, y, MV_DOWN);
7647       started_moving = TRUE;
7648
7649       Feld[x][y] =
7650         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7651          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7652          EL_DC_MAGIC_WALL_FILLING);
7653       Store[x][y] = element;
7654     }
7655     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7656     {
7657       SplashAcid(x, y + 1);
7658
7659       InitMovingField(x, y, MV_DOWN);
7660       started_moving = TRUE;
7661
7662       Store[x][y] = EL_ACID;
7663     }
7664     else if (
7665              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7666               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7667              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7668               CAN_FALL(element) && WasJustFalling[x][y] &&
7669               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7670
7671              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7672               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7673               (Feld[x][y + 1] == EL_BLOCKED)))
7674     {
7675       /* this is needed for a special case not covered by calling "Impact()"
7676          from "ContinueMoving()": if an element moves to a tile directly below
7677          another element which was just falling on that tile (which was empty
7678          in the previous frame), the falling element above would just stop
7679          instead of smashing the element below (in previous version, the above
7680          element was just checked for "moving" instead of "falling", resulting
7681          in incorrect smashes caused by horizontal movement of the above
7682          element; also, the case of the player being the element to smash was
7683          simply not covered here... :-/ ) */
7684
7685       CheckCollision[x][y] = 0;
7686       CheckImpact[x][y] = 0;
7687
7688       Impact(x, y);
7689     }
7690     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7691     {
7692       if (MovDir[x][y] == MV_NONE)
7693       {
7694         InitMovingField(x, y, MV_DOWN);
7695         started_moving = TRUE;
7696       }
7697     }
7698     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7699     {
7700       if (WasJustFalling[x][y]) // prevent animation from being restarted
7701         MovDir[x][y] = MV_DOWN;
7702
7703       InitMovingField(x, y, MV_DOWN);
7704       started_moving = TRUE;
7705     }
7706     else if (element == EL_AMOEBA_DROP)
7707     {
7708       Feld[x][y] = EL_AMOEBA_GROWING;
7709       Store[x][y] = EL_AMOEBA_WET;
7710     }
7711     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7712               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7713              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7714              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7715     {
7716       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7717                                 (IS_FREE(x - 1, y + 1) ||
7718                                  Feld[x - 1][y + 1] == EL_ACID));
7719       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7720                                 (IS_FREE(x + 1, y + 1) ||
7721                                  Feld[x + 1][y + 1] == EL_ACID));
7722       boolean can_fall_any  = (can_fall_left || can_fall_right);
7723       boolean can_fall_both = (can_fall_left && can_fall_right);
7724       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7725
7726       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7727       {
7728         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7729           can_fall_right = FALSE;
7730         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7731           can_fall_left = FALSE;
7732         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7733           can_fall_right = FALSE;
7734         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7735           can_fall_left = FALSE;
7736
7737         can_fall_any  = (can_fall_left || can_fall_right);
7738         can_fall_both = FALSE;
7739       }
7740
7741       if (can_fall_both)
7742       {
7743         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7744           can_fall_right = FALSE;       // slip down on left side
7745         else
7746           can_fall_left = !(can_fall_right = RND(2));
7747
7748         can_fall_both = FALSE;
7749       }
7750
7751       if (can_fall_any)
7752       {
7753         // if not determined otherwise, prefer left side for slipping down
7754         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7755         started_moving = TRUE;
7756       }
7757     }
7758     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7759     {
7760       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
7761       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7762       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7763       int belt_dir = game.belt_dir[belt_nr];
7764
7765       if ((belt_dir == MV_LEFT  && left_is_free) ||
7766           (belt_dir == MV_RIGHT && right_is_free))
7767       {
7768         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7769
7770         InitMovingField(x, y, belt_dir);
7771         started_moving = TRUE;
7772
7773         Pushed[x][y] = TRUE;
7774         Pushed[nextx][y] = TRUE;
7775
7776         GfxAction[x][y] = ACTION_DEFAULT;
7777       }
7778       else
7779       {
7780         MovDir[x][y] = 0;       // if element was moving, stop it
7781       }
7782     }
7783   }
7784
7785   // not "else if" because of elements that can fall and move (EL_SPRING)
7786   if (CAN_MOVE(element) && !started_moving)
7787   {
7788     int move_pattern = element_info[element].move_pattern;
7789     int newx, newy;
7790
7791     Moving2Blocked(x, y, &newx, &newy);
7792
7793     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7794       return;
7795
7796     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7797         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7798     {
7799       WasJustMoving[x][y] = 0;
7800       CheckCollision[x][y] = 0;
7801
7802       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7803
7804       if (Feld[x][y] != element)        // element has changed
7805         return;
7806     }
7807
7808     if (!MovDelay[x][y])        // start new movement phase
7809     {
7810       // all objects that can change their move direction after each step
7811       // (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall
7812
7813       if (element != EL_YAMYAM &&
7814           element != EL_DARK_YAMYAM &&
7815           element != EL_PACMAN &&
7816           !(move_pattern & MV_ANY_DIRECTION) &&
7817           move_pattern != MV_TURNING_LEFT &&
7818           move_pattern != MV_TURNING_RIGHT &&
7819           move_pattern != MV_TURNING_LEFT_RIGHT &&
7820           move_pattern != MV_TURNING_RIGHT_LEFT &&
7821           move_pattern != MV_TURNING_RANDOM)
7822       {
7823         TurnRound(x, y);
7824
7825         if (MovDelay[x][y] && (element == EL_BUG ||
7826                                element == EL_SPACESHIP ||
7827                                element == EL_SP_SNIKSNAK ||
7828                                element == EL_SP_ELECTRON ||
7829                                element == EL_MOLE))
7830           TEST_DrawLevelField(x, y);
7831       }
7832     }
7833
7834     if (MovDelay[x][y])         // wait some time before next movement
7835     {
7836       MovDelay[x][y]--;
7837
7838       if (element == EL_ROBOT ||
7839           element == EL_YAMYAM ||
7840           element == EL_DARK_YAMYAM)
7841       {
7842         DrawLevelElementAnimationIfNeeded(x, y, element);
7843         PlayLevelSoundAction(x, y, ACTION_WAITING);
7844       }
7845       else if (element == EL_SP_ELECTRON)
7846         DrawLevelElementAnimationIfNeeded(x, y, element);
7847       else if (element == EL_DRAGON)
7848       {
7849         int i;
7850         int dir = MovDir[x][y];
7851         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7852         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
7853         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
7854                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
7855                        dir == MV_UP     ? IMG_FLAMES_1_UP :
7856                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7857         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7858
7859         GfxAction[x][y] = ACTION_ATTACKING;
7860
7861         if (IS_PLAYER(x, y))
7862           DrawPlayerField(x, y);
7863         else
7864           TEST_DrawLevelField(x, y);
7865
7866         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7867
7868         for (i = 1; i <= 3; i++)
7869         {
7870           int xx = x + i * dx;
7871           int yy = y + i * dy;
7872           int sx = SCREENX(xx);
7873           int sy = SCREENY(yy);
7874           int flame_graphic = graphic + (i - 1);
7875
7876           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7877             break;
7878
7879           if (MovDelay[x][y])
7880           {
7881             int flamed = MovingOrBlocked2Element(xx, yy);
7882
7883             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7884               Bang(xx, yy);
7885             else
7886               RemoveMovingField(xx, yy);
7887
7888             ChangeDelay[xx][yy] = 0;
7889
7890             Feld[xx][yy] = EL_FLAMES;
7891
7892             if (IN_SCR_FIELD(sx, sy))
7893             {
7894               TEST_DrawLevelFieldCrumbled(xx, yy);
7895               DrawGraphic(sx, sy, flame_graphic, frame);
7896             }
7897           }
7898           else
7899           {
7900             if (Feld[xx][yy] == EL_FLAMES)
7901               Feld[xx][yy] = EL_EMPTY;
7902             TEST_DrawLevelField(xx, yy);
7903           }
7904         }
7905       }
7906
7907       if (MovDelay[x][y])       // element still has to wait some time
7908       {
7909         PlayLevelSoundAction(x, y, ACTION_WAITING);
7910
7911         return;
7912       }
7913     }
7914
7915     // now make next step
7916
7917     Moving2Blocked(x, y, &newx, &newy); // get next screen position
7918
7919     if (DONT_COLLIDE_WITH(element) &&
7920         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7921         !PLAYER_ENEMY_PROTECTED(newx, newy))
7922     {
7923       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7924
7925       return;
7926     }
7927
7928     else if (CAN_MOVE_INTO_ACID(element) &&
7929              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7930              !IS_MV_DIAGONAL(MovDir[x][y]) &&
7931              (MovDir[x][y] == MV_DOWN ||
7932               game.engine_version >= VERSION_IDENT(3,1,0,0)))
7933     {
7934       SplashAcid(newx, newy);
7935       Store[x][y] = EL_ACID;
7936     }
7937     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7938     {
7939       if (Feld[newx][newy] == EL_EXIT_OPEN ||
7940           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7941           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7942           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7943       {
7944         RemoveField(x, y);
7945         TEST_DrawLevelField(x, y);
7946
7947         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7948         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7949           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7950
7951         local_player->friends_still_needed--;
7952         if (!local_player->friends_still_needed &&
7953             !local_player->GameOver && AllPlayersGone)
7954           PlayerWins(local_player);
7955
7956         return;
7957       }
7958       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7959       {
7960         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7961           TEST_DrawLevelField(newx, newy);
7962         else
7963           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7964       }
7965       else if (!IS_FREE(newx, newy))
7966       {
7967         GfxAction[x][y] = ACTION_WAITING;
7968
7969         if (IS_PLAYER(x, y))
7970           DrawPlayerField(x, y);
7971         else
7972           TEST_DrawLevelField(x, y);
7973
7974         return;
7975       }
7976     }
7977     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7978     {
7979       if (IS_FOOD_PIG(Feld[newx][newy]))
7980       {
7981         if (IS_MOVING(newx, newy))
7982           RemoveMovingField(newx, newy);
7983         else
7984         {
7985           Feld[newx][newy] = EL_EMPTY;
7986           TEST_DrawLevelField(newx, newy);
7987         }
7988
7989         PlayLevelSound(x, y, SND_PIG_DIGGING);
7990       }
7991       else if (!IS_FREE(newx, newy))
7992       {
7993         if (IS_PLAYER(x, y))
7994           DrawPlayerField(x, y);
7995         else
7996           TEST_DrawLevelField(x, y);
7997
7998         return;
7999       }
8000     }
8001     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8002     {
8003       if (Store[x][y] != EL_EMPTY)
8004       {
8005         boolean can_clone = FALSE;
8006         int xx, yy;
8007
8008         // check if element to clone is still there
8009         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8010         {
8011           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8012           {
8013             can_clone = TRUE;
8014
8015             break;
8016           }
8017         }
8018
8019         // cannot clone or target field not free anymore -- do not clone
8020         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8021           Store[x][y] = EL_EMPTY;
8022       }
8023
8024       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8025       {
8026         if (IS_MV_DIAGONAL(MovDir[x][y]))
8027         {
8028           int diagonal_move_dir = MovDir[x][y];
8029           int stored = Store[x][y];
8030           int change_delay = 8;
8031           int graphic;
8032
8033           // android is moving diagonally
8034
8035           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8036
8037           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8038           GfxElement[x][y] = EL_EMC_ANDROID;
8039           GfxAction[x][y] = ACTION_SHRINKING;
8040           GfxDir[x][y] = diagonal_move_dir;
8041           ChangeDelay[x][y] = change_delay;
8042
8043           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8044                                    GfxDir[x][y]);
8045
8046           DrawLevelGraphicAnimation(x, y, graphic);
8047           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8048
8049           if (Feld[newx][newy] == EL_ACID)
8050           {
8051             SplashAcid(newx, newy);
8052
8053             return;
8054           }
8055
8056           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8057
8058           Store[newx][newy] = EL_EMC_ANDROID;
8059           GfxElement[newx][newy] = EL_EMC_ANDROID;
8060           GfxAction[newx][newy] = ACTION_GROWING;
8061           GfxDir[newx][newy] = diagonal_move_dir;
8062           ChangeDelay[newx][newy] = change_delay;
8063
8064           graphic = el_act_dir2img(GfxElement[newx][newy],
8065                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8066
8067           DrawLevelGraphicAnimation(newx, newy, graphic);
8068           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8069
8070           return;
8071         }
8072         else
8073         {
8074           Feld[newx][newy] = EL_EMPTY;
8075           TEST_DrawLevelField(newx, newy);
8076
8077           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8078         }
8079       }
8080       else if (!IS_FREE(newx, newy))
8081       {
8082         return;
8083       }
8084     }
8085     else if (IS_CUSTOM_ELEMENT(element) &&
8086              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8087     {
8088       if (!DigFieldByCE(newx, newy, element))
8089         return;
8090
8091       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8092       {
8093         RunnerVisit[x][y] = FrameCounter;
8094         PlayerVisit[x][y] /= 8;         // expire player visit path
8095       }
8096     }
8097     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8098     {
8099       if (!IS_FREE(newx, newy))
8100       {
8101         if (IS_PLAYER(x, y))
8102           DrawPlayerField(x, y);
8103         else
8104           TEST_DrawLevelField(x, y);
8105
8106         return;
8107       }
8108       else
8109       {
8110         boolean wanna_flame = !RND(10);
8111         int dx = newx - x, dy = newy - y;
8112         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8113         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8114         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8115                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8116         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8117                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8118
8119         if ((wanna_flame ||
8120              IS_CLASSIC_ENEMY(element1) ||
8121              IS_CLASSIC_ENEMY(element2)) &&
8122             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8123             element1 != EL_FLAMES && element2 != EL_FLAMES)
8124         {
8125           ResetGfxAnimation(x, y);
8126           GfxAction[x][y] = ACTION_ATTACKING;
8127
8128           if (IS_PLAYER(x, y))
8129             DrawPlayerField(x, y);
8130           else
8131             TEST_DrawLevelField(x, y);
8132
8133           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8134
8135           MovDelay[x][y] = 50;
8136
8137           Feld[newx][newy] = EL_FLAMES;
8138           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8139             Feld[newx1][newy1] = EL_FLAMES;
8140           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8141             Feld[newx2][newy2] = EL_FLAMES;
8142
8143           return;
8144         }
8145       }
8146     }
8147     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8148              Feld[newx][newy] == EL_DIAMOND)
8149     {
8150       if (IS_MOVING(newx, newy))
8151         RemoveMovingField(newx, newy);
8152       else
8153       {
8154         Feld[newx][newy] = EL_EMPTY;
8155         TEST_DrawLevelField(newx, newy);
8156       }
8157
8158       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8159     }
8160     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8161              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8162     {
8163       if (AmoebaNr[newx][newy])
8164       {
8165         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8166         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8167             Feld[newx][newy] == EL_BD_AMOEBA)
8168           AmoebaCnt[AmoebaNr[newx][newy]]--;
8169       }
8170
8171       if (IS_MOVING(newx, newy))
8172       {
8173         RemoveMovingField(newx, newy);
8174       }
8175       else
8176       {
8177         Feld[newx][newy] = EL_EMPTY;
8178         TEST_DrawLevelField(newx, newy);
8179       }
8180
8181       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8182     }
8183     else if ((element == EL_PACMAN || element == EL_MOLE)
8184              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8185     {
8186       if (AmoebaNr[newx][newy])
8187       {
8188         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8189         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8190             Feld[newx][newy] == EL_BD_AMOEBA)
8191           AmoebaCnt[AmoebaNr[newx][newy]]--;
8192       }
8193
8194       if (element == EL_MOLE)
8195       {
8196         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8197         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8198
8199         ResetGfxAnimation(x, y);
8200         GfxAction[x][y] = ACTION_DIGGING;
8201         TEST_DrawLevelField(x, y);
8202
8203         MovDelay[newx][newy] = 0;       // start amoeba shrinking delay
8204
8205         return;                         // wait for shrinking amoeba
8206       }
8207       else      // element == EL_PACMAN
8208       {
8209         Feld[newx][newy] = EL_EMPTY;
8210         TEST_DrawLevelField(newx, newy);
8211         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8212       }
8213     }
8214     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8215              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8216               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8217     {
8218       // wait for shrinking amoeba to completely disappear
8219       return;
8220     }
8221     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8222     {
8223       // object was running against a wall
8224
8225       TurnRound(x, y);
8226
8227       if (GFX_ELEMENT(element) != EL_SAND)     // !!! FIX THIS (crumble) !!!
8228         DrawLevelElementAnimation(x, y, element);
8229
8230       if (DONT_TOUCH(element))
8231         TestIfBadThingTouchesPlayer(x, y);
8232
8233       return;
8234     }
8235
8236     InitMovingField(x, y, MovDir[x][y]);
8237
8238     PlayLevelSoundAction(x, y, ACTION_MOVING);
8239   }
8240
8241   if (MovDir[x][y])
8242     ContinueMoving(x, y);
8243 }
8244
8245 void ContinueMoving(int x, int y)
8246 {
8247   int element = Feld[x][y];
8248   struct ElementInfo *ei = &element_info[element];
8249   int direction = MovDir[x][y];
8250   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8251   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8252   int newx = x + dx, newy = y + dy;
8253   int stored = Store[x][y];
8254   int stored_new = Store[newx][newy];
8255   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8256   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8257   boolean last_line = (newy == lev_fieldy - 1);
8258
8259   MovPos[x][y] += getElementMoveStepsize(x, y);
8260
8261   if (pushed_by_player) // special case: moving object pushed by player
8262     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8263
8264   if (ABS(MovPos[x][y]) < TILEX)
8265   {
8266     TEST_DrawLevelField(x, y);
8267
8268     return;     // element is still moving
8269   }
8270
8271   // element reached destination field
8272
8273   Feld[x][y] = EL_EMPTY;
8274   Feld[newx][newy] = element;
8275   MovPos[x][y] = 0;     // force "not moving" for "crumbled sand"
8276
8277   if (Store[x][y] == EL_ACID)   // element is moving into acid pool
8278   {
8279     element = Feld[newx][newy] = EL_ACID;
8280   }
8281   else if (element == EL_MOLE)
8282   {
8283     Feld[x][y] = EL_SAND;
8284
8285     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8286   }
8287   else if (element == EL_QUICKSAND_FILLING)
8288   {
8289     element = Feld[newx][newy] = get_next_element(element);
8290     Store[newx][newy] = Store[x][y];
8291   }
8292   else if (element == EL_QUICKSAND_EMPTYING)
8293   {
8294     Feld[x][y] = get_next_element(element);
8295     element = Feld[newx][newy] = Store[x][y];
8296   }
8297   else if (element == EL_QUICKSAND_FAST_FILLING)
8298   {
8299     element = Feld[newx][newy] = get_next_element(element);
8300     Store[newx][newy] = Store[x][y];
8301   }
8302   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8303   {
8304     Feld[x][y] = get_next_element(element);
8305     element = Feld[newx][newy] = Store[x][y];
8306   }
8307   else if (element == EL_MAGIC_WALL_FILLING)
8308   {
8309     element = Feld[newx][newy] = get_next_element(element);
8310     if (!game.magic_wall_active)
8311       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8312     Store[newx][newy] = Store[x][y];
8313   }
8314   else if (element == EL_MAGIC_WALL_EMPTYING)
8315   {
8316     Feld[x][y] = get_next_element(element);
8317     if (!game.magic_wall_active)
8318       Feld[x][y] = EL_MAGIC_WALL_DEAD;
8319     element = Feld[newx][newy] = Store[x][y];
8320
8321     InitField(newx, newy, FALSE);
8322   }
8323   else if (element == EL_BD_MAGIC_WALL_FILLING)
8324   {
8325     element = Feld[newx][newy] = get_next_element(element);
8326     if (!game.magic_wall_active)
8327       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8328     Store[newx][newy] = Store[x][y];
8329   }
8330   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8331   {
8332     Feld[x][y] = get_next_element(element);
8333     if (!game.magic_wall_active)
8334       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8335     element = Feld[newx][newy] = Store[x][y];
8336
8337     InitField(newx, newy, FALSE);
8338   }
8339   else if (element == EL_DC_MAGIC_WALL_FILLING)
8340   {
8341     element = Feld[newx][newy] = get_next_element(element);
8342     if (!game.magic_wall_active)
8343       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8344     Store[newx][newy] = Store[x][y];
8345   }
8346   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8347   {
8348     Feld[x][y] = get_next_element(element);
8349     if (!game.magic_wall_active)
8350       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8351     element = Feld[newx][newy] = Store[x][y];
8352
8353     InitField(newx, newy, FALSE);
8354   }
8355   else if (element == EL_AMOEBA_DROPPING)
8356   {
8357     Feld[x][y] = get_next_element(element);
8358     element = Feld[newx][newy] = Store[x][y];
8359   }
8360   else if (element == EL_SOKOBAN_OBJECT)
8361   {
8362     if (Back[x][y])
8363       Feld[x][y] = Back[x][y];
8364
8365     if (Back[newx][newy])
8366       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8367
8368     Back[x][y] = Back[newx][newy] = 0;
8369   }
8370
8371   Store[x][y] = EL_EMPTY;
8372   MovPos[x][y] = 0;
8373   MovDir[x][y] = 0;
8374   MovDelay[x][y] = 0;
8375
8376   MovDelay[newx][newy] = 0;
8377
8378   if (CAN_CHANGE_OR_HAS_ACTION(element))
8379   {
8380     // copy element change control values to new field
8381     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8382     ChangePage[newx][newy]  = ChangePage[x][y];
8383     ChangeCount[newx][newy] = ChangeCount[x][y];
8384     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8385   }
8386
8387   CustomValue[newx][newy] = CustomValue[x][y];
8388
8389   ChangeDelay[x][y] = 0;
8390   ChangePage[x][y] = -1;
8391   ChangeCount[x][y] = 0;
8392   ChangeEvent[x][y] = -1;
8393
8394   CustomValue[x][y] = 0;
8395
8396   // copy animation control values to new field
8397   GfxFrame[newx][newy]  = GfxFrame[x][y];
8398   GfxRandom[newx][newy] = GfxRandom[x][y];      // keep same random value
8399   GfxAction[newx][newy] = GfxAction[x][y];      // keep action one frame
8400   GfxDir[newx][newy]    = GfxDir[x][y];         // keep element direction
8401
8402   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8403
8404   // some elements can leave other elements behind after moving
8405   if (ei->move_leave_element != EL_EMPTY &&
8406       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8407       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8408   {
8409     int move_leave_element = ei->move_leave_element;
8410
8411     // this makes it possible to leave the removed element again
8412     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8413       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8414
8415     Feld[x][y] = move_leave_element;
8416
8417     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8418       MovDir[x][y] = direction;
8419
8420     InitField(x, y, FALSE);
8421
8422     if (GFX_CRUMBLED(Feld[x][y]))
8423       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8424
8425     if (ELEM_IS_PLAYER(move_leave_element))
8426       RelocatePlayer(x, y, move_leave_element);
8427   }
8428
8429   // do this after checking for left-behind element
8430   ResetGfxAnimation(x, y);      // reset animation values for old field
8431
8432   if (!CAN_MOVE(element) ||
8433       (CAN_FALL(element) && direction == MV_DOWN &&
8434        (element == EL_SPRING ||
8435         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8436         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8437     GfxDir[x][y] = MovDir[newx][newy] = 0;
8438
8439   TEST_DrawLevelField(x, y);
8440   TEST_DrawLevelField(newx, newy);
8441
8442   Stop[newx][newy] = TRUE;      // ignore this element until the next frame
8443
8444   // prevent pushed element from moving on in pushed direction
8445   if (pushed_by_player && CAN_MOVE(element) &&
8446       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8447       !(element_info[element].move_pattern & direction))
8448     TurnRound(newx, newy);
8449
8450   // prevent elements on conveyor belt from moving on in last direction
8451   if (pushed_by_conveyor && CAN_FALL(element) &&
8452       direction & MV_HORIZONTAL)
8453     MovDir[newx][newy] = 0;
8454
8455   if (!pushed_by_player)
8456   {
8457     int nextx = newx + dx, nexty = newy + dy;
8458     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8459
8460     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8461
8462     if (CAN_FALL(element) && direction == MV_DOWN)
8463       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8464
8465     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8466       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8467
8468     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8469       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8470   }
8471
8472   if (DONT_TOUCH(element))      // object may be nasty to player or others
8473   {
8474     TestIfBadThingTouchesPlayer(newx, newy);
8475     TestIfBadThingTouchesFriend(newx, newy);
8476
8477     if (!IS_CUSTOM_ELEMENT(element))
8478       TestIfBadThingTouchesOtherBadThing(newx, newy);
8479   }
8480   else if (element == EL_PENGUIN)
8481     TestIfFriendTouchesBadThing(newx, newy);
8482
8483   if (DONT_GET_HIT_BY(element))
8484   {
8485     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8486   }
8487
8488   // give the player one last chance (one more frame) to move away
8489   if (CAN_FALL(element) && direction == MV_DOWN &&
8490       (last_line || (!IS_FREE(x, newy + 1) &&
8491                      (!IS_PLAYER(x, newy + 1) ||
8492                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8493     Impact(x, newy);
8494
8495   if (pushed_by_player && !game.use_change_when_pushing_bug)
8496   {
8497     int push_side = MV_DIR_OPPOSITE(direction);
8498     struct PlayerInfo *player = PLAYERINFO(x, y);
8499
8500     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8501                                player->index_bit, push_side);
8502     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8503                                         player->index_bit, push_side);
8504   }
8505
8506   if (element == EL_EMC_ANDROID && pushed_by_player)    // make another move
8507     MovDelay[newx][newy] = 1;
8508
8509   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8510
8511   TestIfElementTouchesCustomElement(x, y);      // empty or new element
8512   TestIfElementHitsCustomElement(newx, newy, direction);
8513   TestIfPlayerTouchesCustomElement(newx, newy);
8514   TestIfElementTouchesCustomElement(newx, newy);
8515
8516   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8517       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8518     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8519                              MV_DIR_OPPOSITE(direction));
8520 }
8521
8522 int AmoebeNachbarNr(int ax, int ay)
8523 {
8524   int i;
8525   int element = Feld[ax][ay];
8526   int group_nr = 0;
8527   static int xy[4][2] =
8528   {
8529     { 0, -1 },
8530     { -1, 0 },
8531     { +1, 0 },
8532     { 0, +1 }
8533   };
8534
8535   for (i = 0; i < NUM_DIRECTIONS; i++)
8536   {
8537     int x = ax + xy[i][0];
8538     int y = ay + xy[i][1];
8539
8540     if (!IN_LEV_FIELD(x, y))
8541       continue;
8542
8543     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8544       group_nr = AmoebaNr[x][y];
8545   }
8546
8547   return group_nr;
8548 }
8549
8550 static void AmoebenVereinigen(int ax, int ay)
8551 {
8552   int i, x, y, xx, yy;
8553   int new_group_nr = AmoebaNr[ax][ay];
8554   static int xy[4][2] =
8555   {
8556     { 0, -1 },
8557     { -1, 0 },
8558     { +1, 0 },
8559     { 0, +1 }
8560   };
8561
8562   if (new_group_nr == 0)
8563     return;
8564
8565   for (i = 0; i < NUM_DIRECTIONS; i++)
8566   {
8567     x = ax + xy[i][0];
8568     y = ay + xy[i][1];
8569
8570     if (!IN_LEV_FIELD(x, y))
8571       continue;
8572
8573     if ((Feld[x][y] == EL_AMOEBA_FULL ||
8574          Feld[x][y] == EL_BD_AMOEBA ||
8575          Feld[x][y] == EL_AMOEBA_DEAD) &&
8576         AmoebaNr[x][y] != new_group_nr)
8577     {
8578       int old_group_nr = AmoebaNr[x][y];
8579
8580       if (old_group_nr == 0)
8581         return;
8582
8583       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8584       AmoebaCnt[old_group_nr] = 0;
8585       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8586       AmoebaCnt2[old_group_nr] = 0;
8587
8588       SCAN_PLAYFIELD(xx, yy)
8589       {
8590         if (AmoebaNr[xx][yy] == old_group_nr)
8591           AmoebaNr[xx][yy] = new_group_nr;
8592       }
8593     }
8594   }
8595 }
8596
8597 void AmoebeUmwandeln(int ax, int ay)
8598 {
8599   int i, x, y;
8600
8601   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8602   {
8603     int group_nr = AmoebaNr[ax][ay];
8604
8605 #ifdef DEBUG
8606     if (group_nr == 0)
8607     {
8608       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8609       printf("AmoebeUmwandeln(): This should never happen!\n");
8610       return;
8611     }
8612 #endif
8613
8614     SCAN_PLAYFIELD(x, y)
8615     {
8616       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8617       {
8618         AmoebaNr[x][y] = 0;
8619         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8620       }
8621     }
8622
8623     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8624                             SND_AMOEBA_TURNING_TO_GEM :
8625                             SND_AMOEBA_TURNING_TO_ROCK));
8626     Bang(ax, ay);
8627   }
8628   else
8629   {
8630     static int xy[4][2] =
8631     {
8632       { 0, -1 },
8633       { -1, 0 },
8634       { +1, 0 },
8635       { 0, +1 }
8636     };
8637
8638     for (i = 0; i < NUM_DIRECTIONS; i++)
8639     {
8640       x = ax + xy[i][0];
8641       y = ay + xy[i][1];
8642
8643       if (!IN_LEV_FIELD(x, y))
8644         continue;
8645
8646       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8647       {
8648         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8649                               SND_AMOEBA_TURNING_TO_GEM :
8650                               SND_AMOEBA_TURNING_TO_ROCK));
8651         Bang(x, y);
8652       }
8653     }
8654   }
8655 }
8656
8657 static void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8658 {
8659   int x, y;
8660   int group_nr = AmoebaNr[ax][ay];
8661   boolean done = FALSE;
8662
8663 #ifdef DEBUG
8664   if (group_nr == 0)
8665   {
8666     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8667     printf("AmoebeUmwandelnBD(): This should never happen!\n");
8668     return;
8669   }
8670 #endif
8671
8672   SCAN_PLAYFIELD(x, y)
8673   {
8674     if (AmoebaNr[x][y] == group_nr &&
8675         (Feld[x][y] == EL_AMOEBA_DEAD ||
8676          Feld[x][y] == EL_BD_AMOEBA ||
8677          Feld[x][y] == EL_AMOEBA_GROWING))
8678     {
8679       AmoebaNr[x][y] = 0;
8680       Feld[x][y] = new_element;
8681       InitField(x, y, FALSE);
8682       TEST_DrawLevelField(x, y);
8683       done = TRUE;
8684     }
8685   }
8686
8687   if (done)
8688     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8689                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8690                             SND_BD_AMOEBA_TURNING_TO_GEM));
8691 }
8692
8693 static void AmoebeWaechst(int x, int y)
8694 {
8695   static unsigned int sound_delay = 0;
8696   static unsigned int sound_delay_value = 0;
8697
8698   if (!MovDelay[x][y])          // start new growing cycle
8699   {
8700     MovDelay[x][y] = 7;
8701
8702     if (DelayReached(&sound_delay, sound_delay_value))
8703     {
8704       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8705       sound_delay_value = 30;
8706     }
8707   }
8708
8709   if (MovDelay[x][y])           // wait some time before growing bigger
8710   {
8711     MovDelay[x][y]--;
8712     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8713     {
8714       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8715                                            6 - MovDelay[x][y]);
8716
8717       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8718     }
8719
8720     if (!MovDelay[x][y])
8721     {
8722       Feld[x][y] = Store[x][y];
8723       Store[x][y] = 0;
8724       TEST_DrawLevelField(x, y);
8725     }
8726   }
8727 }
8728
8729 static void AmoebaDisappearing(int x, int y)
8730 {
8731   static unsigned int sound_delay = 0;
8732   static unsigned int sound_delay_value = 0;
8733
8734   if (!MovDelay[x][y])          // start new shrinking cycle
8735   {
8736     MovDelay[x][y] = 7;
8737
8738     if (DelayReached(&sound_delay, sound_delay_value))
8739       sound_delay_value = 30;
8740   }
8741
8742   if (MovDelay[x][y])           // wait some time before shrinking
8743   {
8744     MovDelay[x][y]--;
8745     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8746     {
8747       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8748                                            6 - MovDelay[x][y]);
8749
8750       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8751     }
8752
8753     if (!MovDelay[x][y])
8754     {
8755       Feld[x][y] = EL_EMPTY;
8756       TEST_DrawLevelField(x, y);
8757
8758       // don't let mole enter this field in this cycle;
8759       // (give priority to objects falling to this field from above)
8760       Stop[x][y] = TRUE;
8761     }
8762   }
8763 }
8764
8765 static void AmoebeAbleger(int ax, int ay)
8766 {
8767   int i;
8768   int element = Feld[ax][ay];
8769   int graphic = el2img(element);
8770   int newax = ax, neway = ay;
8771   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8772   static int xy[4][2] =
8773   {
8774     { 0, -1 },
8775     { -1, 0 },
8776     { +1, 0 },
8777     { 0, +1 }
8778   };
8779
8780   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8781   {
8782     Feld[ax][ay] = EL_AMOEBA_DEAD;
8783     TEST_DrawLevelField(ax, ay);
8784     return;
8785   }
8786
8787   if (IS_ANIMATED(graphic))
8788     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8789
8790   if (!MovDelay[ax][ay])        // start making new amoeba field
8791     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8792
8793   if (MovDelay[ax][ay])         // wait some time before making new amoeba
8794   {
8795     MovDelay[ax][ay]--;
8796     if (MovDelay[ax][ay])
8797       return;
8798   }
8799
8800   if (can_drop)                 // EL_AMOEBA_WET or EL_EMC_DRIPPER
8801   {
8802     int start = RND(4);
8803     int x = ax + xy[start][0];
8804     int y = ay + xy[start][1];
8805
8806     if (!IN_LEV_FIELD(x, y))
8807       return;
8808
8809     if (IS_FREE(x, y) ||
8810         CAN_GROW_INTO(Feld[x][y]) ||
8811         Feld[x][y] == EL_QUICKSAND_EMPTY ||
8812         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8813     {
8814       newax = x;
8815       neway = y;
8816     }
8817
8818     if (newax == ax && neway == ay)
8819       return;
8820   }
8821   else                          // normal or "filled" (BD style) amoeba
8822   {
8823     int start = RND(4);
8824     boolean waiting_for_player = FALSE;
8825
8826     for (i = 0; i < NUM_DIRECTIONS; i++)
8827     {
8828       int j = (start + i) % 4;
8829       int x = ax + xy[j][0];
8830       int y = ay + xy[j][1];
8831
8832       if (!IN_LEV_FIELD(x, y))
8833         continue;
8834
8835       if (IS_FREE(x, y) ||
8836           CAN_GROW_INTO(Feld[x][y]) ||
8837           Feld[x][y] == EL_QUICKSAND_EMPTY ||
8838           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8839       {
8840         newax = x;
8841         neway = y;
8842         break;
8843       }
8844       else if (IS_PLAYER(x, y))
8845         waiting_for_player = TRUE;
8846     }
8847
8848     if (newax == ax && neway == ay)             // amoeba cannot grow
8849     {
8850       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8851       {
8852         Feld[ax][ay] = EL_AMOEBA_DEAD;
8853         TEST_DrawLevelField(ax, ay);
8854         AmoebaCnt[AmoebaNr[ax][ay]]--;
8855
8856         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   // amoeba is completely dead
8857         {
8858           if (element == EL_AMOEBA_FULL)
8859             AmoebeUmwandeln(ax, ay);
8860           else if (element == EL_BD_AMOEBA)
8861             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8862         }
8863       }
8864       return;
8865     }
8866     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8867     {
8868       // amoeba gets larger by growing in some direction
8869
8870       int new_group_nr = AmoebaNr[ax][ay];
8871
8872 #ifdef DEBUG
8873   if (new_group_nr == 0)
8874   {
8875     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8876     printf("AmoebeAbleger(): This should never happen!\n");
8877     return;
8878   }
8879 #endif
8880
8881       AmoebaNr[newax][neway] = new_group_nr;
8882       AmoebaCnt[new_group_nr]++;
8883       AmoebaCnt2[new_group_nr]++;
8884
8885       // if amoeba touches other amoeba(s) after growing, unify them
8886       AmoebenVereinigen(newax, neway);
8887
8888       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8889       {
8890         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8891         return;
8892       }
8893     }
8894   }
8895
8896   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8897       (neway == lev_fieldy - 1 && newax != ax))
8898   {
8899     Feld[newax][neway] = EL_AMOEBA_GROWING;     // creation of new amoeba
8900     Store[newax][neway] = element;
8901   }
8902   else if (neway == ay || element == EL_EMC_DRIPPER)
8903   {
8904     Feld[newax][neway] = EL_AMOEBA_DROP;        // drop left/right of amoeba
8905
8906     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8907   }
8908   else
8909   {
8910     InitMovingField(ax, ay, MV_DOWN);           // drop dripping from amoeba
8911     Feld[ax][ay] = EL_AMOEBA_DROPPING;
8912     Store[ax][ay] = EL_AMOEBA_DROP;
8913     ContinueMoving(ax, ay);
8914     return;
8915   }
8916
8917   TEST_DrawLevelField(newax, neway);
8918 }
8919
8920 static void Life(int ax, int ay)
8921 {
8922   int x1, y1, x2, y2;
8923   int life_time = 40;
8924   int element = Feld[ax][ay];
8925   int graphic = el2img(element);
8926   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8927                          level.biomaze);
8928   boolean changed = FALSE;
8929
8930   if (IS_ANIMATED(graphic))
8931     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8932
8933   if (Stop[ax][ay])
8934     return;
8935
8936   if (!MovDelay[ax][ay])        // start new "game of life" cycle
8937     MovDelay[ax][ay] = life_time;
8938
8939   if (MovDelay[ax][ay])         // wait some time before next cycle
8940   {
8941     MovDelay[ax][ay]--;
8942     if (MovDelay[ax][ay])
8943       return;
8944   }
8945
8946   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8947   {
8948     int xx = ax+x1, yy = ay+y1;
8949     int old_element = Feld[xx][yy];
8950     int num_neighbours = 0;
8951
8952     if (!IN_LEV_FIELD(xx, yy))
8953       continue;
8954
8955     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8956     {
8957       int x = xx+x2, y = yy+y2;
8958
8959       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8960         continue;
8961
8962       boolean is_player_cell = (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y));
8963       boolean is_neighbour = FALSE;
8964
8965       if (level.use_life_bugs)
8966         is_neighbour =
8967           (((Feld[x][y] == element || is_player_cell) && !Stop[x][y]) ||
8968            (IS_FREE(x, y)                             &&  Stop[x][y]));
8969       else
8970         is_neighbour =
8971           (Last[x][y] == element || is_player_cell);
8972
8973       if (is_neighbour)
8974         num_neighbours++;
8975     }
8976
8977     boolean is_free = FALSE;
8978
8979     if (level.use_life_bugs)
8980       is_free = (IS_FREE(xx, yy));
8981     else
8982       is_free = (IS_FREE(xx, yy) && Last[xx][yy] == EL_EMPTY);
8983
8984     if (xx == ax && yy == ay)           // field in the middle
8985     {
8986       if (num_neighbours < life_parameter[0] ||
8987           num_neighbours > life_parameter[1])
8988       {
8989         Feld[xx][yy] = EL_EMPTY;
8990         if (Feld[xx][yy] != old_element)
8991           TEST_DrawLevelField(xx, yy);
8992         Stop[xx][yy] = TRUE;
8993         changed = TRUE;
8994       }
8995     }
8996     else if (is_free || CAN_GROW_INTO(Feld[xx][yy]))
8997     {                                   // free border field
8998       if (num_neighbours >= life_parameter[2] &&
8999           num_neighbours <= life_parameter[3])
9000       {
9001         Feld[xx][yy] = element;
9002         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9003         if (Feld[xx][yy] != old_element)
9004           TEST_DrawLevelField(xx, yy);
9005         Stop[xx][yy] = TRUE;
9006         changed = TRUE;
9007       }
9008     }
9009   }
9010
9011   if (changed)
9012     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9013                    SND_GAME_OF_LIFE_GROWING);
9014 }
9015
9016 static void InitRobotWheel(int x, int y)
9017 {
9018   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9019 }
9020
9021 static void RunRobotWheel(int x, int y)
9022 {
9023   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9024 }
9025
9026 static void StopRobotWheel(int x, int y)
9027 {
9028   if (ZX == x && ZY == y)
9029   {
9030     ZX = ZY = -1;
9031
9032     game.robot_wheel_active = FALSE;
9033   }
9034 }
9035
9036 static void InitTimegateWheel(int x, int y)
9037 {
9038   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9039 }
9040
9041 static void RunTimegateWheel(int x, int y)
9042 {
9043   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9044 }
9045
9046 static void InitMagicBallDelay(int x, int y)
9047 {
9048   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9049 }
9050
9051 static void ActivateMagicBall(int bx, int by)
9052 {
9053   int x, y;
9054
9055   if (level.ball_random)
9056   {
9057     int pos_border = RND(8);    // select one of the eight border elements
9058     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9059     int xx = pos_content % 3;
9060     int yy = pos_content / 3;
9061
9062     x = bx - 1 + xx;
9063     y = by - 1 + yy;
9064
9065     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9066       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9067   }
9068   else
9069   {
9070     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9071     {
9072       int xx = x - bx + 1;
9073       int yy = y - by + 1;
9074
9075       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9076         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9077     }
9078   }
9079
9080   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9081 }
9082
9083 static void CheckExit(int x, int y)
9084 {
9085   if (local_player->gems_still_needed > 0 ||
9086       local_player->sokoban_fields_still_needed > 0 ||
9087       local_player->sokoban_objects_still_needed > 0 ||
9088       local_player->lights_still_needed > 0)
9089   {
9090     int element = Feld[x][y];
9091     int graphic = el2img(element);
9092
9093     if (IS_ANIMATED(graphic))
9094       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9095
9096     return;
9097   }
9098
9099   if (AllPlayersGone)   // do not re-open exit door closed after last player
9100     return;
9101
9102   Feld[x][y] = EL_EXIT_OPENING;
9103
9104   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9105 }
9106
9107 static void CheckExitEM(int x, int y)
9108 {
9109   if (local_player->gems_still_needed > 0 ||
9110       local_player->sokoban_fields_still_needed > 0 ||
9111       local_player->sokoban_objects_still_needed > 0 ||
9112       local_player->lights_still_needed > 0)
9113   {
9114     int element = Feld[x][y];
9115     int graphic = el2img(element);
9116
9117     if (IS_ANIMATED(graphic))
9118       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9119
9120     return;
9121   }
9122
9123   if (AllPlayersGone)   // do not re-open exit door closed after last player
9124     return;
9125
9126   Feld[x][y] = EL_EM_EXIT_OPENING;
9127
9128   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9129 }
9130
9131 static void CheckExitSteel(int x, int y)
9132 {
9133   if (local_player->gems_still_needed > 0 ||
9134       local_player->sokoban_fields_still_needed > 0 ||
9135       local_player->sokoban_objects_still_needed > 0 ||
9136       local_player->lights_still_needed > 0)
9137   {
9138     int element = Feld[x][y];
9139     int graphic = el2img(element);
9140
9141     if (IS_ANIMATED(graphic))
9142       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9143
9144     return;
9145   }
9146
9147   if (AllPlayersGone)   // do not re-open exit door closed after last player
9148     return;
9149
9150   Feld[x][y] = EL_STEEL_EXIT_OPENING;
9151
9152   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9153 }
9154
9155 static void CheckExitSteelEM(int x, int y)
9156 {
9157   if (local_player->gems_still_needed > 0 ||
9158       local_player->sokoban_fields_still_needed > 0 ||
9159       local_player->sokoban_objects_still_needed > 0 ||
9160       local_player->lights_still_needed > 0)
9161   {
9162     int element = Feld[x][y];
9163     int graphic = el2img(element);
9164
9165     if (IS_ANIMATED(graphic))
9166       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9167
9168     return;
9169   }
9170
9171   if (AllPlayersGone)   // do not re-open exit door closed after last player
9172     return;
9173
9174   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9175
9176   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9177 }
9178
9179 static void CheckExitSP(int x, int y)
9180 {
9181   if (local_player->gems_still_needed > 0)
9182   {
9183     int element = Feld[x][y];
9184     int graphic = el2img(element);
9185
9186     if (IS_ANIMATED(graphic))
9187       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9188
9189     return;
9190   }
9191
9192   if (AllPlayersGone)   // do not re-open exit door closed after last player
9193     return;
9194
9195   Feld[x][y] = EL_SP_EXIT_OPENING;
9196
9197   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9198 }
9199
9200 static void CloseAllOpenTimegates(void)
9201 {
9202   int x, y;
9203
9204   SCAN_PLAYFIELD(x, y)
9205   {
9206     int element = Feld[x][y];
9207
9208     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9209     {
9210       Feld[x][y] = EL_TIMEGATE_CLOSING;
9211
9212       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9213     }
9214   }
9215 }
9216
9217 static void DrawTwinkleOnField(int x, int y)
9218 {
9219   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9220     return;
9221
9222   if (Feld[x][y] == EL_BD_DIAMOND)
9223     return;
9224
9225   if (MovDelay[x][y] == 0)      // next animation frame
9226     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9227
9228   if (MovDelay[x][y] != 0)      // wait some time before next frame
9229   {
9230     MovDelay[x][y]--;
9231
9232     DrawLevelElementAnimation(x, y, Feld[x][y]);
9233
9234     if (MovDelay[x][y] != 0)
9235     {
9236       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9237                                            10 - MovDelay[x][y]);
9238
9239       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9240     }
9241   }
9242 }
9243
9244 static void MauerWaechst(int x, int y)
9245 {
9246   int delay = 6;
9247
9248   if (!MovDelay[x][y])          // next animation frame
9249     MovDelay[x][y] = 3 * delay;
9250
9251   if (MovDelay[x][y])           // wait some time before next frame
9252   {
9253     MovDelay[x][y]--;
9254
9255     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9256     {
9257       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9258       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9259
9260       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9261     }
9262
9263     if (!MovDelay[x][y])
9264     {
9265       if (MovDir[x][y] == MV_LEFT)
9266       {
9267         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9268           TEST_DrawLevelField(x - 1, y);
9269       }
9270       else if (MovDir[x][y] == MV_RIGHT)
9271       {
9272         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9273           TEST_DrawLevelField(x + 1, y);
9274       }
9275       else if (MovDir[x][y] == MV_UP)
9276       {
9277         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9278           TEST_DrawLevelField(x, y - 1);
9279       }
9280       else
9281       {
9282         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9283           TEST_DrawLevelField(x, y + 1);
9284       }
9285
9286       Feld[x][y] = Store[x][y];
9287       Store[x][y] = 0;
9288       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9289       TEST_DrawLevelField(x, y);
9290     }
9291   }
9292 }
9293
9294 static void MauerAbleger(int ax, int ay)
9295 {
9296   int element = Feld[ax][ay];
9297   int graphic = el2img(element);
9298   boolean oben_frei = FALSE, unten_frei = FALSE;
9299   boolean links_frei = FALSE, rechts_frei = FALSE;
9300   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9301   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9302   boolean new_wall = FALSE;
9303
9304   if (IS_ANIMATED(graphic))
9305     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9306
9307   if (!MovDelay[ax][ay])        // start building new wall
9308     MovDelay[ax][ay] = 6;
9309
9310   if (MovDelay[ax][ay])         // wait some time before building new wall
9311   {
9312     MovDelay[ax][ay]--;
9313     if (MovDelay[ax][ay])
9314       return;
9315   }
9316
9317   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9318     oben_frei = TRUE;
9319   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9320     unten_frei = TRUE;
9321   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9322     links_frei = TRUE;
9323   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9324     rechts_frei = TRUE;
9325
9326   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9327       element == EL_EXPANDABLE_WALL_ANY)
9328   {
9329     if (oben_frei)
9330     {
9331       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9332       Store[ax][ay-1] = element;
9333       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9334       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9335         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9336                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9337       new_wall = TRUE;
9338     }
9339     if (unten_frei)
9340     {
9341       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9342       Store[ax][ay+1] = element;
9343       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9344       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9345         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9346                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9347       new_wall = TRUE;
9348     }
9349   }
9350
9351   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9352       element == EL_EXPANDABLE_WALL_ANY ||
9353       element == EL_EXPANDABLE_WALL ||
9354       element == EL_BD_EXPANDABLE_WALL)
9355   {
9356     if (links_frei)
9357     {
9358       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9359       Store[ax-1][ay] = element;
9360       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9361       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9362         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9363                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9364       new_wall = TRUE;
9365     }
9366
9367     if (rechts_frei)
9368     {
9369       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9370       Store[ax+1][ay] = element;
9371       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9372       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9373         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9374                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9375       new_wall = TRUE;
9376     }
9377   }
9378
9379   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9380     TEST_DrawLevelField(ax, ay);
9381
9382   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9383     oben_massiv = TRUE;
9384   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9385     unten_massiv = TRUE;
9386   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9387     links_massiv = TRUE;
9388   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9389     rechts_massiv = TRUE;
9390
9391   if (((oben_massiv && unten_massiv) ||
9392        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9393        element == EL_EXPANDABLE_WALL) &&
9394       ((links_massiv && rechts_massiv) ||
9395        element == EL_EXPANDABLE_WALL_VERTICAL))
9396     Feld[ax][ay] = EL_WALL;
9397
9398   if (new_wall)
9399     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9400 }
9401
9402 static void MauerAblegerStahl(int ax, int ay)
9403 {
9404   int element = Feld[ax][ay];
9405   int graphic = el2img(element);
9406   boolean oben_frei = FALSE, unten_frei = FALSE;
9407   boolean links_frei = FALSE, rechts_frei = FALSE;
9408   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9409   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9410   boolean new_wall = FALSE;
9411
9412   if (IS_ANIMATED(graphic))
9413     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9414
9415   if (!MovDelay[ax][ay])        // start building new wall
9416     MovDelay[ax][ay] = 6;
9417
9418   if (MovDelay[ax][ay])         // wait some time before building new wall
9419   {
9420     MovDelay[ax][ay]--;
9421     if (MovDelay[ax][ay])
9422       return;
9423   }
9424
9425   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9426     oben_frei = TRUE;
9427   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9428     unten_frei = TRUE;
9429   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9430     links_frei = TRUE;
9431   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9432     rechts_frei = TRUE;
9433
9434   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9435       element == EL_EXPANDABLE_STEELWALL_ANY)
9436   {
9437     if (oben_frei)
9438     {
9439       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9440       Store[ax][ay-1] = element;
9441       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9442       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9443         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9444                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9445       new_wall = TRUE;
9446     }
9447     if (unten_frei)
9448     {
9449       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9450       Store[ax][ay+1] = element;
9451       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9452       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9453         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9454                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9455       new_wall = TRUE;
9456     }
9457   }
9458
9459   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9460       element == EL_EXPANDABLE_STEELWALL_ANY)
9461   {
9462     if (links_frei)
9463     {
9464       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9465       Store[ax-1][ay] = element;
9466       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9467       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9468         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9469                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9470       new_wall = TRUE;
9471     }
9472
9473     if (rechts_frei)
9474     {
9475       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9476       Store[ax+1][ay] = element;
9477       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9478       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9479         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9480                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9481       new_wall = TRUE;
9482     }
9483   }
9484
9485   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9486     oben_massiv = TRUE;
9487   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9488     unten_massiv = TRUE;
9489   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9490     links_massiv = TRUE;
9491   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9492     rechts_massiv = TRUE;
9493
9494   if (((oben_massiv && unten_massiv) ||
9495        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9496       ((links_massiv && rechts_massiv) ||
9497        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9498     Feld[ax][ay] = EL_STEELWALL;
9499
9500   if (new_wall)
9501     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9502 }
9503
9504 static void CheckForDragon(int x, int y)
9505 {
9506   int i, j;
9507   boolean dragon_found = FALSE;
9508   static int xy[4][2] =
9509   {
9510     { 0, -1 },
9511     { -1, 0 },
9512     { +1, 0 },
9513     { 0, +1 }
9514   };
9515
9516   for (i = 0; i < NUM_DIRECTIONS; i++)
9517   {
9518     for (j = 0; j < 4; j++)
9519     {
9520       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9521
9522       if (IN_LEV_FIELD(xx, yy) &&
9523           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9524       {
9525         if (Feld[xx][yy] == EL_DRAGON)
9526           dragon_found = TRUE;
9527       }
9528       else
9529         break;
9530     }
9531   }
9532
9533   if (!dragon_found)
9534   {
9535     for (i = 0; i < NUM_DIRECTIONS; i++)
9536     {
9537       for (j = 0; j < 3; j++)
9538       {
9539         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9540   
9541         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9542         {
9543           Feld[xx][yy] = EL_EMPTY;
9544           TEST_DrawLevelField(xx, yy);
9545         }
9546         else
9547           break;
9548       }
9549     }
9550   }
9551 }
9552
9553 static void InitBuggyBase(int x, int y)
9554 {
9555   int element = Feld[x][y];
9556   int activating_delay = FRAMES_PER_SECOND / 4;
9557
9558   ChangeDelay[x][y] =
9559     (element == EL_SP_BUGGY_BASE ?
9560      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9561      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9562      activating_delay :
9563      element == EL_SP_BUGGY_BASE_ACTIVE ?
9564      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9565 }
9566
9567 static void WarnBuggyBase(int x, int y)
9568 {
9569   int i;
9570   static int xy[4][2] =
9571   {
9572     { 0, -1 },
9573     { -1, 0 },
9574     { +1, 0 },
9575     { 0, +1 }
9576   };
9577
9578   for (i = 0; i < NUM_DIRECTIONS; i++)
9579   {
9580     int xx = x + xy[i][0];
9581     int yy = y + xy[i][1];
9582
9583     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9584     {
9585       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9586
9587       break;
9588     }
9589   }
9590 }
9591
9592 static void InitTrap(int x, int y)
9593 {
9594   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9595 }
9596
9597 static void ActivateTrap(int x, int y)
9598 {
9599   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9600 }
9601
9602 static void ChangeActiveTrap(int x, int y)
9603 {
9604   int graphic = IMG_TRAP_ACTIVE;
9605
9606   // if new animation frame was drawn, correct crumbled sand border
9607   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9608     TEST_DrawLevelFieldCrumbled(x, y);
9609 }
9610
9611 static int getSpecialActionElement(int element, int number, int base_element)
9612 {
9613   return (element != EL_EMPTY ? element :
9614           number != -1 ? base_element + number - 1 :
9615           EL_EMPTY);
9616 }
9617
9618 static int getModifiedActionNumber(int value_old, int operator, int operand,
9619                                    int value_min, int value_max)
9620 {
9621   int value_new = (operator == CA_MODE_SET      ? operand :
9622                    operator == CA_MODE_ADD      ? value_old + operand :
9623                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9624                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9625                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9626                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9627                    value_old);
9628
9629   return (value_new < value_min ? value_min :
9630           value_new > value_max ? value_max :
9631           value_new);
9632 }
9633
9634 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9635 {
9636   struct ElementInfo *ei = &element_info[element];
9637   struct ElementChangeInfo *change = &ei->change_page[page];
9638   int target_element = change->target_element;
9639   int action_type = change->action_type;
9640   int action_mode = change->action_mode;
9641   int action_arg = change->action_arg;
9642   int action_element = change->action_element;
9643   int i;
9644
9645   if (!change->has_action)
9646     return;
9647
9648   // ---------- determine action paramater values -----------------------------
9649
9650   int level_time_value =
9651     (level.time > 0 ? TimeLeft :
9652      TimePlayed);
9653
9654   int action_arg_element_raw =
9655     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9656      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9657      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9658      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
9659      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9660      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
9661      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
9662      EL_EMPTY);
9663   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9664
9665   int action_arg_direction =
9666     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9667      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9668      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9669      change->actual_trigger_side :
9670      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9671      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9672      MV_NONE);
9673
9674   int action_arg_number_min =
9675     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9676      CA_ARG_MIN);
9677
9678   int action_arg_number_max =
9679     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9680      action_type == CA_SET_LEVEL_GEMS ? 999 :
9681      action_type == CA_SET_LEVEL_TIME ? 9999 :
9682      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9683      action_type == CA_SET_CE_VALUE ? 9999 :
9684      action_type == CA_SET_CE_SCORE ? 9999 :
9685      CA_ARG_MAX);
9686
9687   int action_arg_number_reset =
9688     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9689      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9690      action_type == CA_SET_LEVEL_TIME ? level.time :
9691      action_type == CA_SET_LEVEL_SCORE ? 0 :
9692      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9693      action_type == CA_SET_CE_SCORE ? 0 :
9694      0);
9695
9696   int action_arg_number =
9697     (action_arg <= CA_ARG_MAX ? action_arg :
9698      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9699      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9700      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9701      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9702      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9703      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9704      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9705      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9706      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9707      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9708      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9709      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9710      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9711      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9712      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9713      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9714      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9715      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9716      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
9717      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9718      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
9719      -1);
9720
9721   int action_arg_number_old =
9722     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9723      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9724      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
9725      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9726      action_type == CA_SET_CE_SCORE ? ei->collect_score :
9727      0);
9728
9729   int action_arg_number_new =
9730     getModifiedActionNumber(action_arg_number_old,
9731                             action_mode, action_arg_number,
9732                             action_arg_number_min, action_arg_number_max);
9733
9734   int trigger_player_bits =
9735     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9736      change->actual_trigger_player_bits : change->trigger_player);
9737
9738   int action_arg_player_bits =
9739     (action_arg >= CA_ARG_PLAYER_1 &&
9740      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9741      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9742      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9743      PLAYER_BITS_ANY);
9744
9745   // ---------- execute action  -----------------------------------------------
9746
9747   switch (action_type)
9748   {
9749     case CA_NO_ACTION:
9750     {
9751       return;
9752     }
9753
9754     // ---------- level actions  ----------------------------------------------
9755
9756     case CA_RESTART_LEVEL:
9757     {
9758       game.restart_level = TRUE;
9759
9760       break;
9761     }
9762
9763     case CA_SHOW_ENVELOPE:
9764     {
9765       int element = getSpecialActionElement(action_arg_element,
9766                                             action_arg_number, EL_ENVELOPE_1);
9767
9768       if (IS_ENVELOPE(element))
9769         local_player->show_envelope = element;
9770
9771       break;
9772     }
9773
9774     case CA_SET_LEVEL_TIME:
9775     {
9776       if (level.time > 0)       // only modify limited time value
9777       {
9778         TimeLeft = action_arg_number_new;
9779
9780         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9781
9782         DisplayGameControlValues();
9783
9784         if (!TimeLeft && setup.time_limit)
9785           for (i = 0; i < MAX_PLAYERS; i++)
9786             KillPlayer(&stored_player[i]);
9787       }
9788
9789       break;
9790     }
9791
9792     case CA_SET_LEVEL_SCORE:
9793     {
9794       local_player->score = action_arg_number_new;
9795
9796       game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
9797
9798       DisplayGameControlValues();
9799
9800       break;
9801     }
9802
9803     case CA_SET_LEVEL_GEMS:
9804     {
9805       local_player->gems_still_needed = action_arg_number_new;
9806
9807       game.snapshot.collected_item = TRUE;
9808
9809       game_panel_controls[GAME_PANEL_GEMS].value =
9810         local_player->gems_still_needed;
9811
9812       DisplayGameControlValues();
9813
9814       break;
9815     }
9816
9817     case CA_SET_LEVEL_WIND:
9818     {
9819       game.wind_direction = action_arg_direction;
9820
9821       break;
9822     }
9823
9824     case CA_SET_LEVEL_RANDOM_SEED:
9825     {
9826       // ensure that setting a new random seed while playing is predictable
9827       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
9828
9829       break;
9830     }
9831
9832     // ---------- player actions  ---------------------------------------------
9833
9834     case CA_MOVE_PLAYER:
9835     {
9836       // automatically move to the next field in specified direction
9837       for (i = 0; i < MAX_PLAYERS; i++)
9838         if (trigger_player_bits & (1 << i))
9839           stored_player[i].programmed_action = action_arg_direction;
9840
9841       break;
9842     }
9843
9844     case CA_EXIT_PLAYER:
9845     {
9846       for (i = 0; i < MAX_PLAYERS; i++)
9847         if (action_arg_player_bits & (1 << i))
9848           ExitPlayer(&stored_player[i]);
9849
9850       if (AllPlayersGone)
9851         PlayerWins(local_player);
9852
9853       break;
9854     }
9855
9856     case CA_KILL_PLAYER:
9857     {
9858       for (i = 0; i < MAX_PLAYERS; i++)
9859         if (action_arg_player_bits & (1 << i))
9860           KillPlayer(&stored_player[i]);
9861
9862       break;
9863     }
9864
9865     case CA_SET_PLAYER_KEYS:
9866     {
9867       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9868       int element = getSpecialActionElement(action_arg_element,
9869                                             action_arg_number, EL_KEY_1);
9870
9871       if (IS_KEY(element))
9872       {
9873         for (i = 0; i < MAX_PLAYERS; i++)
9874         {
9875           if (trigger_player_bits & (1 << i))
9876           {
9877             stored_player[i].key[KEY_NR(element)] = key_state;
9878
9879             DrawGameDoorValues();
9880           }
9881         }
9882       }
9883
9884       break;
9885     }
9886
9887     case CA_SET_PLAYER_SPEED:
9888     {
9889       for (i = 0; i < MAX_PLAYERS; i++)
9890       {
9891         if (trigger_player_bits & (1 << i))
9892         {
9893           int move_stepsize = TILEX / stored_player[i].move_delay_value;
9894
9895           if (action_arg == CA_ARG_SPEED_FASTER &&
9896               stored_player[i].cannot_move)
9897           {
9898             action_arg_number = STEPSIZE_VERY_SLOW;
9899           }
9900           else if (action_arg == CA_ARG_SPEED_SLOWER ||
9901                    action_arg == CA_ARG_SPEED_FASTER)
9902           {
9903             action_arg_number = 2;
9904             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9905                            CA_MODE_MULTIPLY);
9906           }
9907           else if (action_arg == CA_ARG_NUMBER_RESET)
9908           {
9909             action_arg_number = level.initial_player_stepsize[i];
9910           }
9911
9912           move_stepsize =
9913             getModifiedActionNumber(move_stepsize,
9914                                     action_mode,
9915                                     action_arg_number,
9916                                     action_arg_number_min,
9917                                     action_arg_number_max);
9918
9919           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9920         }
9921       }
9922
9923       break;
9924     }
9925
9926     case CA_SET_PLAYER_SHIELD:
9927     {
9928       for (i = 0; i < MAX_PLAYERS; i++)
9929       {
9930         if (trigger_player_bits & (1 << i))
9931         {
9932           if (action_arg == CA_ARG_SHIELD_OFF)
9933           {
9934             stored_player[i].shield_normal_time_left = 0;
9935             stored_player[i].shield_deadly_time_left = 0;
9936           }
9937           else if (action_arg == CA_ARG_SHIELD_NORMAL)
9938           {
9939             stored_player[i].shield_normal_time_left = 999999;
9940           }
9941           else if (action_arg == CA_ARG_SHIELD_DEADLY)
9942           {
9943             stored_player[i].shield_normal_time_left = 999999;
9944             stored_player[i].shield_deadly_time_left = 999999;
9945           }
9946         }
9947       }
9948
9949       break;
9950     }
9951
9952     case CA_SET_PLAYER_GRAVITY:
9953     {
9954       for (i = 0; i < MAX_PLAYERS; i++)
9955       {
9956         if (trigger_player_bits & (1 << i))
9957         {
9958           stored_player[i].gravity =
9959             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
9960              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
9961              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9962              stored_player[i].gravity);
9963         }
9964       }
9965
9966       break;
9967     }
9968
9969     case CA_SET_PLAYER_ARTWORK:
9970     {
9971       for (i = 0; i < MAX_PLAYERS; i++)
9972       {
9973         if (trigger_player_bits & (1 << i))
9974         {
9975           int artwork_element = action_arg_element;
9976
9977           if (action_arg == CA_ARG_ELEMENT_RESET)
9978             artwork_element =
9979               (level.use_artwork_element[i] ? level.artwork_element[i] :
9980                stored_player[i].element_nr);
9981
9982           if (stored_player[i].artwork_element != artwork_element)
9983             stored_player[i].Frame = 0;
9984
9985           stored_player[i].artwork_element = artwork_element;
9986
9987           SetPlayerWaiting(&stored_player[i], FALSE);
9988
9989           // set number of special actions for bored and sleeping animation
9990           stored_player[i].num_special_action_bored =
9991             get_num_special_action(artwork_element,
9992                                    ACTION_BORING_1, ACTION_BORING_LAST);
9993           stored_player[i].num_special_action_sleeping =
9994             get_num_special_action(artwork_element,
9995                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
9996         }
9997       }
9998
9999       break;
10000     }
10001
10002     case CA_SET_PLAYER_INVENTORY:
10003     {
10004       for (i = 0; i < MAX_PLAYERS; i++)
10005       {
10006         struct PlayerInfo *player = &stored_player[i];
10007         int j, k;
10008
10009         if (trigger_player_bits & (1 << i))
10010         {
10011           int inventory_element = action_arg_element;
10012
10013           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10014               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10015               action_arg == CA_ARG_ELEMENT_ACTION)
10016           {
10017             int element = inventory_element;
10018             int collect_count = element_info[element].collect_count_initial;
10019
10020             if (!IS_CUSTOM_ELEMENT(element))
10021               collect_count = 1;
10022
10023             if (collect_count == 0)
10024               player->inventory_infinite_element = element;
10025             else
10026               for (k = 0; k < collect_count; k++)
10027                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10028                   player->inventory_element[player->inventory_size++] =
10029                     element;
10030           }
10031           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10032                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10033                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10034           {
10035             if (player->inventory_infinite_element != EL_UNDEFINED &&
10036                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10037                                      action_arg_element_raw))
10038               player->inventory_infinite_element = EL_UNDEFINED;
10039
10040             for (k = 0, j = 0; j < player->inventory_size; j++)
10041             {
10042               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10043                                         action_arg_element_raw))
10044                 player->inventory_element[k++] = player->inventory_element[j];
10045             }
10046
10047             player->inventory_size = k;
10048           }
10049           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10050           {
10051             if (player->inventory_size > 0)
10052             {
10053               for (j = 0; j < player->inventory_size - 1; j++)
10054                 player->inventory_element[j] = player->inventory_element[j + 1];
10055
10056               player->inventory_size--;
10057             }
10058           }
10059           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10060           {
10061             if (player->inventory_size > 0)
10062               player->inventory_size--;
10063           }
10064           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10065           {
10066             player->inventory_infinite_element = EL_UNDEFINED;
10067             player->inventory_size = 0;
10068           }
10069           else if (action_arg == CA_ARG_INVENTORY_RESET)
10070           {
10071             player->inventory_infinite_element = EL_UNDEFINED;
10072             player->inventory_size = 0;
10073
10074             if (level.use_initial_inventory[i])
10075             {
10076               for (j = 0; j < level.initial_inventory_size[i]; j++)
10077               {
10078                 int element = level.initial_inventory_content[i][j];
10079                 int collect_count = element_info[element].collect_count_initial;
10080
10081                 if (!IS_CUSTOM_ELEMENT(element))
10082                   collect_count = 1;
10083
10084                 if (collect_count == 0)
10085                   player->inventory_infinite_element = element;
10086                 else
10087                   for (k = 0; k < collect_count; k++)
10088                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10089                       player->inventory_element[player->inventory_size++] =
10090                         element;
10091               }
10092             }
10093           }
10094         }
10095       }
10096
10097       break;
10098     }
10099
10100     // ---------- CE actions  -------------------------------------------------
10101
10102     case CA_SET_CE_VALUE:
10103     {
10104       int last_ce_value = CustomValue[x][y];
10105
10106       CustomValue[x][y] = action_arg_number_new;
10107
10108       if (CustomValue[x][y] != last_ce_value)
10109       {
10110         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10111         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10112
10113         if (CustomValue[x][y] == 0)
10114         {
10115           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10116           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10117         }
10118       }
10119
10120       break;
10121     }
10122
10123     case CA_SET_CE_SCORE:
10124     {
10125       int last_ce_score = ei->collect_score;
10126
10127       ei->collect_score = action_arg_number_new;
10128
10129       if (ei->collect_score != last_ce_score)
10130       {
10131         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10132         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10133
10134         if (ei->collect_score == 0)
10135         {
10136           int xx, yy;
10137
10138           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10139           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10140
10141           /*
10142             This is a very special case that seems to be a mixture between
10143             CheckElementChange() and CheckTriggeredElementChange(): while
10144             the first one only affects single elements that are triggered
10145             directly, the second one affects multiple elements in the playfield
10146             that are triggered indirectly by another element. This is a third
10147             case: Changing the CE score always affects multiple identical CEs,
10148             so every affected CE must be checked, not only the single CE for
10149             which the CE score was changed in the first place (as every instance
10150             of that CE shares the same CE score, and therefore also can change)!
10151           */
10152           SCAN_PLAYFIELD(xx, yy)
10153           {
10154             if (Feld[xx][yy] == element)
10155               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10156                                  CE_SCORE_GETS_ZERO);
10157           }
10158         }
10159       }
10160
10161       break;
10162     }
10163
10164     case CA_SET_CE_ARTWORK:
10165     {
10166       int artwork_element = action_arg_element;
10167       boolean reset_frame = FALSE;
10168       int xx, yy;
10169
10170       if (action_arg == CA_ARG_ELEMENT_RESET)
10171         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10172                            element);
10173
10174       if (ei->gfx_element != artwork_element)
10175         reset_frame = TRUE;
10176
10177       ei->gfx_element = artwork_element;
10178
10179       SCAN_PLAYFIELD(xx, yy)
10180       {
10181         if (Feld[xx][yy] == element)
10182         {
10183           if (reset_frame)
10184           {
10185             ResetGfxAnimation(xx, yy);
10186             ResetRandomAnimationValue(xx, yy);
10187           }
10188
10189           TEST_DrawLevelField(xx, yy);
10190         }
10191       }
10192
10193       break;
10194     }
10195
10196     // ---------- engine actions  ---------------------------------------------
10197
10198     case CA_SET_ENGINE_SCAN_MODE:
10199     {
10200       InitPlayfieldScanMode(action_arg);
10201
10202       break;
10203     }
10204
10205     default:
10206       break;
10207   }
10208 }
10209
10210 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10211 {
10212   int old_element = Feld[x][y];
10213   int new_element = GetElementFromGroupElement(element);
10214   int previous_move_direction = MovDir[x][y];
10215   int last_ce_value = CustomValue[x][y];
10216   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10217   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10218   boolean add_player_onto_element = (new_element_is_player &&
10219                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10220                                      IS_WALKABLE(old_element));
10221
10222   if (!add_player_onto_element)
10223   {
10224     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10225       RemoveMovingField(x, y);
10226     else
10227       RemoveField(x, y);
10228
10229     Feld[x][y] = new_element;
10230
10231     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10232       MovDir[x][y] = previous_move_direction;
10233
10234     if (element_info[new_element].use_last_ce_value)
10235       CustomValue[x][y] = last_ce_value;
10236
10237     InitField_WithBug1(x, y, FALSE);
10238
10239     new_element = Feld[x][y];   // element may have changed
10240
10241     ResetGfxAnimation(x, y);
10242     ResetRandomAnimationValue(x, y);
10243
10244     TEST_DrawLevelField(x, y);
10245
10246     if (GFX_CRUMBLED(new_element))
10247       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10248   }
10249
10250   // check if element under the player changes from accessible to unaccessible
10251   // (needed for special case of dropping element which then changes)
10252   // (must be checked after creating new element for walkable group elements)
10253   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10254       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10255   {
10256     Bang(x, y);
10257
10258     return;
10259   }
10260
10261   // "ChangeCount" not set yet to allow "entered by player" change one time
10262   if (new_element_is_player)
10263     RelocatePlayer(x, y, new_element);
10264
10265   if (is_change)
10266     ChangeCount[x][y]++;        // count number of changes in the same frame
10267
10268   TestIfBadThingTouchesPlayer(x, y);
10269   TestIfPlayerTouchesCustomElement(x, y);
10270   TestIfElementTouchesCustomElement(x, y);
10271 }
10272
10273 static void CreateField(int x, int y, int element)
10274 {
10275   CreateFieldExt(x, y, element, FALSE);
10276 }
10277
10278 static void CreateElementFromChange(int x, int y, int element)
10279 {
10280   element = GET_VALID_RUNTIME_ELEMENT(element);
10281
10282   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10283   {
10284     int old_element = Feld[x][y];
10285
10286     // prevent changed element from moving in same engine frame
10287     // unless both old and new element can either fall or move
10288     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10289         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10290       Stop[x][y] = TRUE;
10291   }
10292
10293   CreateFieldExt(x, y, element, TRUE);
10294 }
10295
10296 static boolean ChangeElement(int x, int y, int element, int page)
10297 {
10298   struct ElementInfo *ei = &element_info[element];
10299   struct ElementChangeInfo *change = &ei->change_page[page];
10300   int ce_value = CustomValue[x][y];
10301   int ce_score = ei->collect_score;
10302   int target_element;
10303   int old_element = Feld[x][y];
10304
10305   // always use default change event to prevent running into a loop
10306   if (ChangeEvent[x][y] == -1)
10307     ChangeEvent[x][y] = CE_DELAY;
10308
10309   if (ChangeEvent[x][y] == CE_DELAY)
10310   {
10311     // reset actual trigger element, trigger player and action element
10312     change->actual_trigger_element = EL_EMPTY;
10313     change->actual_trigger_player = EL_EMPTY;
10314     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10315     change->actual_trigger_side = CH_SIDE_NONE;
10316     change->actual_trigger_ce_value = 0;
10317     change->actual_trigger_ce_score = 0;
10318   }
10319
10320   // do not change elements more than a specified maximum number of changes
10321   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10322     return FALSE;
10323
10324   ChangeCount[x][y]++;          // count number of changes in the same frame
10325
10326   if (change->explode)
10327   {
10328     Bang(x, y);
10329
10330     return TRUE;
10331   }
10332
10333   if (change->use_target_content)
10334   {
10335     boolean complete_replace = TRUE;
10336     boolean can_replace[3][3];
10337     int xx, yy;
10338
10339     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10340     {
10341       boolean is_empty;
10342       boolean is_walkable;
10343       boolean is_diggable;
10344       boolean is_collectible;
10345       boolean is_removable;
10346       boolean is_destructible;
10347       int ex = x + xx - 1;
10348       int ey = y + yy - 1;
10349       int content_element = change->target_content.e[xx][yy];
10350       int e;
10351
10352       can_replace[xx][yy] = TRUE;
10353
10354       if (ex == x && ey == y)   // do not check changing element itself
10355         continue;
10356
10357       if (content_element == EL_EMPTY_SPACE)
10358       {
10359         can_replace[xx][yy] = FALSE;    // do not replace border with space
10360
10361         continue;
10362       }
10363
10364       if (!IN_LEV_FIELD(ex, ey))
10365       {
10366         can_replace[xx][yy] = FALSE;
10367         complete_replace = FALSE;
10368
10369         continue;
10370       }
10371
10372       e = Feld[ex][ey];
10373
10374       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10375         e = MovingOrBlocked2Element(ex, ey);
10376
10377       is_empty = (IS_FREE(ex, ey) ||
10378                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10379
10380       is_walkable     = (is_empty || IS_WALKABLE(e));
10381       is_diggable     = (is_empty || IS_DIGGABLE(e));
10382       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10383       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10384       is_removable    = (is_diggable || is_collectible);
10385
10386       can_replace[xx][yy] =
10387         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10388           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10389           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10390           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10391           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10392           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10393          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10394
10395       if (!can_replace[xx][yy])
10396         complete_replace = FALSE;
10397     }
10398
10399     if (!change->only_if_complete || complete_replace)
10400     {
10401       boolean something_has_changed = FALSE;
10402
10403       if (change->only_if_complete && change->use_random_replace &&
10404           RND(100) < change->random_percentage)
10405         return FALSE;
10406
10407       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10408       {
10409         int ex = x + xx - 1;
10410         int ey = y + yy - 1;
10411         int content_element;
10412
10413         if (can_replace[xx][yy] && (!change->use_random_replace ||
10414                                     RND(100) < change->random_percentage))
10415         {
10416           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10417             RemoveMovingField(ex, ey);
10418
10419           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10420
10421           content_element = change->target_content.e[xx][yy];
10422           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10423                                               ce_value, ce_score);
10424
10425           CreateElementFromChange(ex, ey, target_element);
10426
10427           something_has_changed = TRUE;
10428
10429           // for symmetry reasons, freeze newly created border elements
10430           if (ex != x || ey != y)
10431             Stop[ex][ey] = TRUE;        // no more moving in this frame
10432         }
10433       }
10434
10435       if (something_has_changed)
10436       {
10437         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10438         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10439       }
10440     }
10441   }
10442   else
10443   {
10444     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10445                                         ce_value, ce_score);
10446
10447     if (element == EL_DIAGONAL_GROWING ||
10448         element == EL_DIAGONAL_SHRINKING)
10449     {
10450       target_element = Store[x][y];
10451
10452       Store[x][y] = EL_EMPTY;
10453     }
10454
10455     CreateElementFromChange(x, y, target_element);
10456
10457     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10458     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10459   }
10460
10461   // this uses direct change before indirect change
10462   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10463
10464   return TRUE;
10465 }
10466
10467 static void HandleElementChange(int x, int y, int page)
10468 {
10469   int element = MovingOrBlocked2Element(x, y);
10470   struct ElementInfo *ei = &element_info[element];
10471   struct ElementChangeInfo *change = &ei->change_page[page];
10472   boolean handle_action_before_change = FALSE;
10473
10474 #ifdef DEBUG
10475   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10476       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10477   {
10478     printf("\n\n");
10479     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10480            x, y, element, element_info[element].token_name);
10481     printf("HandleElementChange(): This should never happen!\n");
10482     printf("\n\n");
10483   }
10484 #endif
10485
10486   // this can happen with classic bombs on walkable, changing elements
10487   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10488   {
10489     return;
10490   }
10491
10492   if (ChangeDelay[x][y] == 0)           // initialize element change
10493   {
10494     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10495
10496     if (change->can_change)
10497     {
10498       // !!! not clear why graphic animation should be reset at all here !!!
10499       // !!! UPDATE: but is needed for correct Snake Bite tail animation !!!
10500       // !!! SOLUTION: do not reset if graphics engine set to 4 or above !!!
10501
10502       /*
10503         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10504
10505         When using an animation frame delay of 1 (this only happens with
10506         "sp_zonk.moving.left/right" in the classic graphics), the default
10507         (non-moving) animation shows wrong animation frames (while the
10508         moving animation, like "sp_zonk.moving.left/right", is correct,
10509         so this graphical bug never shows up with the classic graphics).
10510         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10511         be drawn instead of the correct frames 0,1,2,3. This is caused by
10512         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10513         an element change: First when the change delay ("ChangeDelay[][]")
10514         counter has reached zero after decrementing, then a second time in
10515         the next frame (after "GfxFrame[][]" was already incremented) when
10516         "ChangeDelay[][]" is reset to the initial delay value again.
10517
10518         This causes frame 0 to be drawn twice, while the last frame won't
10519         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10520
10521         As some animations may already be cleverly designed around this bug
10522         (at least the "Snake Bite" snake tail animation does this), it cannot
10523         simply be fixed here without breaking such existing animations.
10524         Unfortunately, it cannot easily be detected if a graphics set was
10525         designed "before" or "after" the bug was fixed. As a workaround,
10526         a new graphics set option "game.graphics_engine_version" was added
10527         to be able to specify the game's major release version for which the
10528         graphics set was designed, which can then be used to decide if the
10529         bugfix should be used (version 4 and above) or not (version 3 or
10530         below, or if no version was specified at all, as with old sets).
10531
10532         (The wrong/fixed animation frames can be tested with the test level set
10533         "test_gfxframe" and level "000", which contains a specially prepared
10534         custom element at level position (x/y) == (11/9) which uses the zonk
10535         animation mentioned above. Using "game.graphics_engine_version: 4"
10536         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10537         This can also be seen from the debug output for this test element.)
10538       */
10539
10540       // when a custom element is about to change (for example by change delay),
10541       // do not reset graphic animation when the custom element is moving
10542       if (game.graphics_engine_version < 4 &&
10543           !IS_MOVING(x, y))
10544       {
10545         ResetGfxAnimation(x, y);
10546         ResetRandomAnimationValue(x, y);
10547       }
10548
10549       if (change->pre_change_function)
10550         change->pre_change_function(x, y);
10551     }
10552   }
10553
10554   ChangeDelay[x][y]--;
10555
10556   if (ChangeDelay[x][y] != 0)           // continue element change
10557   {
10558     if (change->can_change)
10559     {
10560       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10561
10562       if (IS_ANIMATED(graphic))
10563         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10564
10565       if (change->change_function)
10566         change->change_function(x, y);
10567     }
10568   }
10569   else                                  // finish element change
10570   {
10571     if (ChangePage[x][y] != -1)         // remember page from delayed change
10572     {
10573       page = ChangePage[x][y];
10574       ChangePage[x][y] = -1;
10575
10576       change = &ei->change_page[page];
10577     }
10578
10579     if (IS_MOVING(x, y))                // never change a running system ;-)
10580     {
10581       ChangeDelay[x][y] = 1;            // try change after next move step
10582       ChangePage[x][y] = page;          // remember page to use for change
10583
10584       return;
10585     }
10586
10587     // special case: set new level random seed before changing element
10588     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10589       handle_action_before_change = TRUE;
10590
10591     if (change->has_action && handle_action_before_change)
10592       ExecuteCustomElementAction(x, y, element, page);
10593
10594     if (change->can_change)
10595     {
10596       if (ChangeElement(x, y, element, page))
10597       {
10598         if (change->post_change_function)
10599           change->post_change_function(x, y);
10600       }
10601     }
10602
10603     if (change->has_action && !handle_action_before_change)
10604       ExecuteCustomElementAction(x, y, element, page);
10605   }
10606 }
10607
10608 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10609                                               int trigger_element,
10610                                               int trigger_event,
10611                                               int trigger_player,
10612                                               int trigger_side,
10613                                               int trigger_page)
10614 {
10615   boolean change_done_any = FALSE;
10616   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10617   int i;
10618
10619   if (!(trigger_events[trigger_element][trigger_event]))
10620     return FALSE;
10621
10622   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10623
10624   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10625   {
10626     int element = EL_CUSTOM_START + i;
10627     boolean change_done = FALSE;
10628     int p;
10629
10630     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10631         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10632       continue;
10633
10634     for (p = 0; p < element_info[element].num_change_pages; p++)
10635     {
10636       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10637
10638       if (change->can_change_or_has_action &&
10639           change->has_event[trigger_event] &&
10640           change->trigger_side & trigger_side &&
10641           change->trigger_player & trigger_player &&
10642           change->trigger_page & trigger_page_bits &&
10643           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10644       {
10645         change->actual_trigger_element = trigger_element;
10646         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10647         change->actual_trigger_player_bits = trigger_player;
10648         change->actual_trigger_side = trigger_side;
10649         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10650         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10651
10652         if ((change->can_change && !change_done) || change->has_action)
10653         {
10654           int x, y;
10655
10656           SCAN_PLAYFIELD(x, y)
10657           {
10658             if (Feld[x][y] == element)
10659             {
10660               if (change->can_change && !change_done)
10661               {
10662                 // if element already changed in this frame, not only prevent
10663                 // another element change (checked in ChangeElement()), but
10664                 // also prevent additional element actions for this element
10665
10666                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10667                     !level.use_action_after_change_bug)
10668                   continue;
10669
10670                 ChangeDelay[x][y] = 1;
10671                 ChangeEvent[x][y] = trigger_event;
10672
10673                 HandleElementChange(x, y, p);
10674               }
10675               else if (change->has_action)
10676               {
10677                 // if element already changed in this frame, not only prevent
10678                 // another element change (checked in ChangeElement()), but
10679                 // also prevent additional element actions for this element
10680
10681                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10682                     !level.use_action_after_change_bug)
10683                   continue;
10684
10685                 ExecuteCustomElementAction(x, y, element, p);
10686                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10687               }
10688             }
10689           }
10690
10691           if (change->can_change)
10692           {
10693             change_done = TRUE;
10694             change_done_any = TRUE;
10695           }
10696         }
10697       }
10698     }
10699   }
10700
10701   RECURSION_LOOP_DETECTION_END();
10702
10703   return change_done_any;
10704 }
10705
10706 static boolean CheckElementChangeExt(int x, int y,
10707                                      int element,
10708                                      int trigger_element,
10709                                      int trigger_event,
10710                                      int trigger_player,
10711                                      int trigger_side)
10712 {
10713   boolean change_done = FALSE;
10714   int p;
10715
10716   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10717       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10718     return FALSE;
10719
10720   if (Feld[x][y] == EL_BLOCKED)
10721   {
10722     Blocked2Moving(x, y, &x, &y);
10723     element = Feld[x][y];
10724   }
10725
10726   // check if element has already changed or is about to change after moving
10727   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10728        Feld[x][y] != element) ||
10729
10730       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10731        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10732         ChangePage[x][y] != -1)))
10733     return FALSE;
10734
10735   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10736
10737   for (p = 0; p < element_info[element].num_change_pages; p++)
10738   {
10739     struct ElementChangeInfo *change = &element_info[element].change_page[p];
10740
10741     /* check trigger element for all events where the element that is checked
10742        for changing interacts with a directly adjacent element -- this is
10743        different to element changes that affect other elements to change on the
10744        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10745     boolean check_trigger_element =
10746       (trigger_event == CE_TOUCHING_X ||
10747        trigger_event == CE_HITTING_X ||
10748        trigger_event == CE_HIT_BY_X ||
10749        trigger_event == CE_DIGGING_X); // this one was forgotten until 3.2.3
10750
10751     if (change->can_change_or_has_action &&
10752         change->has_event[trigger_event] &&
10753         change->trigger_side & trigger_side &&
10754         change->trigger_player & trigger_player &&
10755         (!check_trigger_element ||
10756          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10757     {
10758       change->actual_trigger_element = trigger_element;
10759       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10760       change->actual_trigger_player_bits = trigger_player;
10761       change->actual_trigger_side = trigger_side;
10762       change->actual_trigger_ce_value = CustomValue[x][y];
10763       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10764
10765       // special case: trigger element not at (x,y) position for some events
10766       if (check_trigger_element)
10767       {
10768         static struct
10769         {
10770           int dx, dy;
10771         } move_xy[] =
10772           {
10773             {  0,  0 },
10774             { -1,  0 },
10775             { +1,  0 },
10776             {  0,  0 },
10777             {  0, -1 },
10778             {  0,  0 }, { 0, 0 }, { 0, 0 },
10779             {  0, +1 }
10780           };
10781
10782         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10783         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10784
10785         change->actual_trigger_ce_value = CustomValue[xx][yy];
10786         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10787       }
10788
10789       if (change->can_change && !change_done)
10790       {
10791         ChangeDelay[x][y] = 1;
10792         ChangeEvent[x][y] = trigger_event;
10793
10794         HandleElementChange(x, y, p);
10795
10796         change_done = TRUE;
10797       }
10798       else if (change->has_action)
10799       {
10800         ExecuteCustomElementAction(x, y, element, p);
10801         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10802       }
10803     }
10804   }
10805
10806   RECURSION_LOOP_DETECTION_END();
10807
10808   return change_done;
10809 }
10810
10811 static void PlayPlayerSound(struct PlayerInfo *player)
10812 {
10813   int jx = player->jx, jy = player->jy;
10814   int sound_element = player->artwork_element;
10815   int last_action = player->last_action_waiting;
10816   int action = player->action_waiting;
10817
10818   if (player->is_waiting)
10819   {
10820     if (action != last_action)
10821       PlayLevelSoundElementAction(jx, jy, sound_element, action);
10822     else
10823       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10824   }
10825   else
10826   {
10827     if (action != last_action)
10828       StopSound(element_info[sound_element].sound[last_action]);
10829
10830     if (last_action == ACTION_SLEEPING)
10831       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10832   }
10833 }
10834
10835 static void PlayAllPlayersSound(void)
10836 {
10837   int i;
10838
10839   for (i = 0; i < MAX_PLAYERS; i++)
10840     if (stored_player[i].active)
10841       PlayPlayerSound(&stored_player[i]);
10842 }
10843
10844 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10845 {
10846   boolean last_waiting = player->is_waiting;
10847   int move_dir = player->MovDir;
10848
10849   player->dir_waiting = move_dir;
10850   player->last_action_waiting = player->action_waiting;
10851
10852   if (is_waiting)
10853   {
10854     if (!last_waiting)          // not waiting -> waiting
10855     {
10856       player->is_waiting = TRUE;
10857
10858       player->frame_counter_bored =
10859         FrameCounter +
10860         game.player_boring_delay_fixed +
10861         GetSimpleRandom(game.player_boring_delay_random);
10862       player->frame_counter_sleeping =
10863         FrameCounter +
10864         game.player_sleeping_delay_fixed +
10865         GetSimpleRandom(game.player_sleeping_delay_random);
10866
10867       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10868     }
10869
10870     if (game.player_sleeping_delay_fixed +
10871         game.player_sleeping_delay_random > 0 &&
10872         player->anim_delay_counter == 0 &&
10873         player->post_delay_counter == 0 &&
10874         FrameCounter >= player->frame_counter_sleeping)
10875       player->is_sleeping = TRUE;
10876     else if (game.player_boring_delay_fixed +
10877              game.player_boring_delay_random > 0 &&
10878              FrameCounter >= player->frame_counter_bored)
10879       player->is_bored = TRUE;
10880
10881     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10882                               player->is_bored ? ACTION_BORING :
10883                               ACTION_WAITING);
10884
10885     if (player->is_sleeping && player->use_murphy)
10886     {
10887       // special case for sleeping Murphy when leaning against non-free tile
10888
10889       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10890           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10891            !IS_MOVING(player->jx - 1, player->jy)))
10892         move_dir = MV_LEFT;
10893       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10894                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10895                 !IS_MOVING(player->jx + 1, player->jy)))
10896         move_dir = MV_RIGHT;
10897       else
10898         player->is_sleeping = FALSE;
10899
10900       player->dir_waiting = move_dir;
10901     }
10902
10903     if (player->is_sleeping)
10904     {
10905       if (player->num_special_action_sleeping > 0)
10906       {
10907         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10908         {
10909           int last_special_action = player->special_action_sleeping;
10910           int num_special_action = player->num_special_action_sleeping;
10911           int special_action =
10912             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10913              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10914              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10915              last_special_action + 1 : ACTION_SLEEPING);
10916           int special_graphic =
10917             el_act_dir2img(player->artwork_element, special_action, move_dir);
10918
10919           player->anim_delay_counter =
10920             graphic_info[special_graphic].anim_delay_fixed +
10921             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10922           player->post_delay_counter =
10923             graphic_info[special_graphic].post_delay_fixed +
10924             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10925
10926           player->special_action_sleeping = special_action;
10927         }
10928
10929         if (player->anim_delay_counter > 0)
10930         {
10931           player->action_waiting = player->special_action_sleeping;
10932           player->anim_delay_counter--;
10933         }
10934         else if (player->post_delay_counter > 0)
10935         {
10936           player->post_delay_counter--;
10937         }
10938       }
10939     }
10940     else if (player->is_bored)
10941     {
10942       if (player->num_special_action_bored > 0)
10943       {
10944         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10945         {
10946           int special_action =
10947             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10948           int special_graphic =
10949             el_act_dir2img(player->artwork_element, special_action, move_dir);
10950
10951           player->anim_delay_counter =
10952             graphic_info[special_graphic].anim_delay_fixed +
10953             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10954           player->post_delay_counter =
10955             graphic_info[special_graphic].post_delay_fixed +
10956             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10957
10958           player->special_action_bored = special_action;
10959         }
10960
10961         if (player->anim_delay_counter > 0)
10962         {
10963           player->action_waiting = player->special_action_bored;
10964           player->anim_delay_counter--;
10965         }
10966         else if (player->post_delay_counter > 0)
10967         {
10968           player->post_delay_counter--;
10969         }
10970       }
10971     }
10972   }
10973   else if (last_waiting)        // waiting -> not waiting
10974   {
10975     player->is_waiting = FALSE;
10976     player->is_bored = FALSE;
10977     player->is_sleeping = FALSE;
10978
10979     player->frame_counter_bored = -1;
10980     player->frame_counter_sleeping = -1;
10981
10982     player->anim_delay_counter = 0;
10983     player->post_delay_counter = 0;
10984
10985     player->dir_waiting = player->MovDir;
10986     player->action_waiting = ACTION_DEFAULT;
10987
10988     player->special_action_bored = ACTION_DEFAULT;
10989     player->special_action_sleeping = ACTION_DEFAULT;
10990   }
10991 }
10992
10993 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
10994 {
10995   if ((!player->is_moving  && player->was_moving) ||
10996       (player->MovPos == 0 && player->was_moving) ||
10997       (player->is_snapping && !player->was_snapping) ||
10998       (player->is_dropping && !player->was_dropping))
10999   {
11000     if (!CheckSaveEngineSnapshotToList())
11001       return;
11002
11003     player->was_moving = FALSE;
11004     player->was_snapping = TRUE;
11005     player->was_dropping = TRUE;
11006   }
11007   else
11008   {
11009     if (player->is_moving)
11010       player->was_moving = TRUE;
11011
11012     if (!player->is_snapping)
11013       player->was_snapping = FALSE;
11014
11015     if (!player->is_dropping)
11016       player->was_dropping = FALSE;
11017   }
11018 }
11019
11020 static void CheckSingleStepMode(struct PlayerInfo *player)
11021 {
11022   if (tape.single_step && tape.recording && !tape.pausing)
11023   {
11024     /* as it is called "single step mode", just return to pause mode when the
11025        player stopped moving after one tile (or never starts moving at all) */
11026     if (!player->is_moving &&
11027         !player->is_pushing &&
11028         !player->is_dropping_pressed)
11029     {
11030       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11031       SnapField(player, 0, 0);                  // stop snapping
11032     }
11033   }
11034
11035   CheckSaveEngineSnapshot(player);
11036 }
11037
11038 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11039 {
11040   int left      = player_action & JOY_LEFT;
11041   int right     = player_action & JOY_RIGHT;
11042   int up        = player_action & JOY_UP;
11043   int down      = player_action & JOY_DOWN;
11044   int button1   = player_action & JOY_BUTTON_1;
11045   int button2   = player_action & JOY_BUTTON_2;
11046   int dx        = (left ? -1 : right ? 1 : 0);
11047   int dy        = (up   ? -1 : down  ? 1 : 0);
11048
11049   if (!player->active || tape.pausing)
11050     return 0;
11051
11052   if (player_action)
11053   {
11054     if (button1)
11055       SnapField(player, dx, dy);
11056     else
11057     {
11058       if (button2)
11059         DropElement(player);
11060
11061       MovePlayer(player, dx, dy);
11062     }
11063
11064     CheckSingleStepMode(player);
11065
11066     SetPlayerWaiting(player, FALSE);
11067
11068     return player_action;
11069   }
11070   else
11071   {
11072     // no actions for this player (no input at player's configured device)
11073
11074     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11075     SnapField(player, 0, 0);
11076     CheckGravityMovementWhenNotMoving(player);
11077
11078     if (player->MovPos == 0)
11079       SetPlayerWaiting(player, TRUE);
11080
11081     if (player->MovPos == 0)    // needed for tape.playing
11082       player->is_moving = FALSE;
11083
11084     player->is_dropping = FALSE;
11085     player->is_dropping_pressed = FALSE;
11086     player->drop_pressed_delay = 0;
11087
11088     CheckSingleStepMode(player);
11089
11090     return 0;
11091   }
11092 }
11093
11094 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11095                                          byte *tape_action)
11096 {
11097   if (!tape.use_mouse)
11098     return;
11099
11100   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11101   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11102   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11103 }
11104
11105 static void SetTapeActionFromMouseAction(byte *tape_action,
11106                                          struct MouseActionInfo *mouse_action)
11107 {
11108   if (!tape.use_mouse)
11109     return;
11110
11111   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11112   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11113   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11114 }
11115
11116 static void CheckLevelSolved(void)
11117 {
11118   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11119   {
11120     if (level.native_em_level->lev->home == 0)  // all players at home
11121     {
11122       PlayerWins(local_player);
11123
11124       AllPlayersGone = TRUE;
11125
11126       level.native_em_level->lev->home = -1;
11127     }
11128
11129     if (level.native_em_level->ply[0]->alive == 0 &&
11130         level.native_em_level->ply[1]->alive == 0 &&
11131         level.native_em_level->ply[2]->alive == 0 &&
11132         level.native_em_level->ply[3]->alive == 0)      // all dead
11133       AllPlayersGone = TRUE;
11134   }
11135   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11136   {
11137     if (game_sp.LevelSolved &&
11138         !game_sp.GameOver)                              // game won
11139     {
11140       PlayerWins(local_player);
11141
11142       game_sp.GameOver = TRUE;
11143
11144       AllPlayersGone = TRUE;
11145     }
11146
11147     if (game_sp.GameOver)                               // game lost
11148       AllPlayersGone = TRUE;
11149   }
11150   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11151   {
11152     if (game_mm.level_solved &&
11153         !game_mm.game_over)                             // game won
11154     {
11155       PlayerWins(local_player);
11156
11157       game_mm.game_over = TRUE;
11158
11159       AllPlayersGone = TRUE;
11160     }
11161
11162     if (game_mm.game_over)                              // game lost
11163       AllPlayersGone = TRUE;
11164   }
11165 }
11166
11167 static void CheckLevelTime(void)
11168 {
11169   int i;
11170
11171   if (TimeFrames >= FRAMES_PER_SECOND)
11172   {
11173     TimeFrames = 0;
11174     TapeTime++;
11175
11176     for (i = 0; i < MAX_PLAYERS; i++)
11177     {
11178       struct PlayerInfo *player = &stored_player[i];
11179
11180       if (SHIELD_ON(player))
11181       {
11182         player->shield_normal_time_left--;
11183
11184         if (player->shield_deadly_time_left > 0)
11185           player->shield_deadly_time_left--;
11186       }
11187     }
11188
11189     if (!local_player->LevelSolved && !level.use_step_counter)
11190     {
11191       TimePlayed++;
11192
11193       if (TimeLeft > 0)
11194       {
11195         TimeLeft--;
11196
11197         if (TimeLeft <= 10 && setup.time_limit)
11198           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11199
11200         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11201            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11202
11203         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11204
11205         if (!TimeLeft && setup.time_limit)
11206         {
11207           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11208             level.native_em_level->lev->killed_out_of_time = TRUE;
11209           else
11210             for (i = 0; i < MAX_PLAYERS; i++)
11211               KillPlayer(&stored_player[i]);
11212         }
11213       }
11214       else if (game.no_time_limit && !AllPlayersGone) // level w/o time limit
11215       {
11216         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11217       }
11218
11219       level.native_em_level->lev->time =
11220         (game.no_time_limit ? TimePlayed : TimeLeft);
11221     }
11222
11223     if (tape.recording || tape.playing)
11224       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11225   }
11226
11227   if (tape.recording || tape.playing)
11228     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11229
11230   UpdateAndDisplayGameControlValues();
11231 }
11232
11233 void AdvanceFrameAndPlayerCounters(int player_nr)
11234 {
11235   int i;
11236
11237   // advance frame counters (global frame counter and time frame counter)
11238   FrameCounter++;
11239   TimeFrames++;
11240
11241   // advance player counters (counters for move delay, move animation etc.)
11242   for (i = 0; i < MAX_PLAYERS; i++)
11243   {
11244     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11245     int move_delay_value = stored_player[i].move_delay_value;
11246     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11247
11248     if (!advance_player_counters)       // not all players may be affected
11249       continue;
11250
11251     if (move_frames == 0)       // less than one move per game frame
11252     {
11253       int stepsize = TILEX / move_delay_value;
11254       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11255       int count = (stored_player[i].is_moving ?
11256                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11257
11258       if (count % delay == 0)
11259         move_frames = 1;
11260     }
11261
11262     stored_player[i].Frame += move_frames;
11263
11264     if (stored_player[i].MovPos != 0)
11265       stored_player[i].StepFrame += move_frames;
11266
11267     if (stored_player[i].move_delay > 0)
11268       stored_player[i].move_delay--;
11269
11270     // due to bugs in previous versions, counter must count up, not down
11271     if (stored_player[i].push_delay != -1)
11272       stored_player[i].push_delay++;
11273
11274     if (stored_player[i].drop_delay > 0)
11275       stored_player[i].drop_delay--;
11276
11277     if (stored_player[i].is_dropping_pressed)
11278       stored_player[i].drop_pressed_delay++;
11279   }
11280 }
11281
11282 void StartGameActions(boolean init_network_game, boolean record_tape,
11283                       int random_seed)
11284 {
11285   unsigned int new_random_seed = InitRND(random_seed);
11286
11287   if (record_tape)
11288     TapeStartRecording(new_random_seed);
11289
11290   if (init_network_game)
11291   {
11292     SendToServer_LevelFile();
11293     SendToServer_StartPlaying();
11294
11295     return;
11296   }
11297
11298   InitGame();
11299 }
11300
11301 static void GameActionsExt(void)
11302 {
11303 #if 0
11304   static unsigned int game_frame_delay = 0;
11305 #endif
11306   unsigned int game_frame_delay_value;
11307   byte *recorded_player_action;
11308   byte summarized_player_action = 0;
11309   byte tape_action[MAX_PLAYERS];
11310   int i;
11311
11312   // detect endless loops, caused by custom element programming
11313   if (recursion_loop_detected && recursion_loop_depth == 0)
11314   {
11315     char *message = getStringCat3("Internal Error! Element ",
11316                                   EL_NAME(recursion_loop_element),
11317                                   " caused endless loop! Quit the game?");
11318
11319     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11320           EL_NAME(recursion_loop_element));
11321
11322     RequestQuitGameExt(FALSE, level_editor_test_game, message);
11323
11324     recursion_loop_detected = FALSE;    // if game should be continued
11325
11326     free(message);
11327
11328     return;
11329   }
11330
11331   if (game.restart_level)
11332     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11333
11334   CheckLevelSolved();
11335
11336   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11337     GameWon();
11338
11339   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11340     TapeStop();
11341
11342   if (game_status != GAME_MODE_PLAYING)         // status might have changed
11343     return;
11344
11345   game_frame_delay_value =
11346     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11347
11348   if (tape.playing && tape.warp_forward && !tape.pausing)
11349     game_frame_delay_value = 0;
11350
11351   SetVideoFrameDelay(game_frame_delay_value);
11352
11353 #if 0
11354 #if 0
11355   // ---------- main game synchronization point ----------
11356
11357   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11358
11359   printf("::: skip == %d\n", skip);
11360
11361 #else
11362   // ---------- main game synchronization point ----------
11363
11364   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11365 #endif
11366 #endif
11367
11368   if (network_playing && !network_player_action_received)
11369   {
11370     // try to get network player actions in time
11371
11372     // last chance to get network player actions without main loop delay
11373     HandleNetworking();
11374
11375     // game was quit by network peer
11376     if (game_status != GAME_MODE_PLAYING)
11377       return;
11378
11379     // check if network player actions still missing and game still running
11380     if (!network_player_action_received && !checkGameEnded())
11381       return;           // failed to get network player actions in time
11382
11383     // do not yet reset "network_player_action_received" (for tape.pausing)
11384   }
11385
11386   if (tape.pausing)
11387     return;
11388
11389   // at this point we know that we really continue executing the game
11390
11391   network_player_action_received = FALSE;
11392
11393   // when playing tape, read previously recorded player input from tape data
11394   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11395
11396   local_player->effective_mouse_action = local_player->mouse_action;
11397
11398   if (recorded_player_action != NULL)
11399     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11400                                  recorded_player_action);
11401
11402   // TapePlayAction() may return NULL when toggling to "pause before death"
11403   if (tape.pausing)
11404     return;
11405
11406   if (tape.set_centered_player)
11407   {
11408     game.centered_player_nr_next = tape.centered_player_nr_next;
11409     game.set_centered_player = TRUE;
11410   }
11411
11412   for (i = 0; i < MAX_PLAYERS; i++)
11413   {
11414     summarized_player_action |= stored_player[i].action;
11415
11416     if (!network_playing && (game.team_mode || tape.playing))
11417       stored_player[i].effective_action = stored_player[i].action;
11418   }
11419
11420   if (network_playing && !checkGameEnded())
11421     SendToServer_MovePlayer(summarized_player_action);
11422
11423   // summarize all actions at local players mapped input device position
11424   // (this allows using different input devices in single player mode)
11425   if (!network.enabled && !game.team_mode)
11426     stored_player[map_player_action[local_player->index_nr]].effective_action =
11427       summarized_player_action;
11428
11429   if (tape.recording &&
11430       setup.team_mode &&
11431       setup.input_on_focus &&
11432       game.centered_player_nr != -1)
11433   {
11434     for (i = 0; i < MAX_PLAYERS; i++)
11435       stored_player[i].effective_action =
11436         (i == game.centered_player_nr ? summarized_player_action : 0);
11437   }
11438
11439   if (recorded_player_action != NULL)
11440     for (i = 0; i < MAX_PLAYERS; i++)
11441       stored_player[i].effective_action = recorded_player_action[i];
11442
11443   for (i = 0; i < MAX_PLAYERS; i++)
11444   {
11445     tape_action[i] = stored_player[i].effective_action;
11446
11447     /* (this may happen in the RND game engine if a player was not present on
11448        the playfield on level start, but appeared later from a custom element */
11449     if (setup.team_mode &&
11450         tape.recording &&
11451         tape_action[i] &&
11452         !tape.player_participates[i])
11453       tape.player_participates[i] = TRUE;
11454   }
11455
11456   SetTapeActionFromMouseAction(tape_action,
11457                                &local_player->effective_mouse_action);
11458
11459   // only record actions from input devices, but not programmed actions
11460   if (tape.recording)
11461     TapeRecordAction(tape_action);
11462
11463 #if USE_NEW_PLAYER_ASSIGNMENTS
11464   // !!! also map player actions in single player mode !!!
11465   // if (game.team_mode)
11466   if (1)
11467   {
11468     byte mapped_action[MAX_PLAYERS];
11469
11470 #if DEBUG_PLAYER_ACTIONS
11471     printf(":::");
11472     for (i = 0; i < MAX_PLAYERS; i++)
11473       printf(" %d, ", stored_player[i].effective_action);
11474 #endif
11475
11476     for (i = 0; i < MAX_PLAYERS; i++)
11477       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11478
11479     for (i = 0; i < MAX_PLAYERS; i++)
11480       stored_player[i].effective_action = mapped_action[i];
11481
11482 #if DEBUG_PLAYER_ACTIONS
11483     printf(" =>");
11484     for (i = 0; i < MAX_PLAYERS; i++)
11485       printf(" %d, ", stored_player[i].effective_action);
11486     printf("\n");
11487 #endif
11488   }
11489 #if DEBUG_PLAYER_ACTIONS
11490   else
11491   {
11492     printf(":::");
11493     for (i = 0; i < MAX_PLAYERS; i++)
11494       printf(" %d, ", stored_player[i].effective_action);
11495     printf("\n");
11496   }
11497 #endif
11498 #endif
11499
11500   for (i = 0; i < MAX_PLAYERS; i++)
11501   {
11502     // allow engine snapshot in case of changed movement attempt
11503     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11504         (stored_player[i].effective_action & KEY_MOTION))
11505       game.snapshot.changed_action = TRUE;
11506
11507     // allow engine snapshot in case of snapping/dropping attempt
11508     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11509         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11510       game.snapshot.changed_action = TRUE;
11511
11512     game.snapshot.last_action[i] = stored_player[i].effective_action;
11513   }
11514
11515   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11516   {
11517     GameActions_EM_Main();
11518   }
11519   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11520   {
11521     GameActions_SP_Main();
11522   }
11523   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11524   {
11525     GameActions_MM_Main();
11526   }
11527   else
11528   {
11529     GameActions_RND_Main();
11530   }
11531
11532   BlitScreenToBitmap(backbuffer);
11533
11534   CheckLevelSolved();
11535   CheckLevelTime();
11536
11537   AdvanceFrameAndPlayerCounters(-1);    // advance counters for all players
11538
11539   if (global.show_frames_per_second)
11540   {
11541     static unsigned int fps_counter = 0;
11542     static int fps_frames = 0;
11543     unsigned int fps_delay_ms = Counter() - fps_counter;
11544
11545     fps_frames++;
11546
11547     if (fps_delay_ms >= 500)    // calculate FPS every 0.5 seconds
11548     {
11549       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11550
11551       fps_frames = 0;
11552       fps_counter = Counter();
11553
11554       // always draw FPS to screen after FPS value was updated
11555       redraw_mask |= REDRAW_FPS;
11556     }
11557
11558     // only draw FPS if no screen areas are deactivated (invisible warp mode)
11559     if (GetDrawDeactivationMask() == REDRAW_NONE)
11560       redraw_mask |= REDRAW_FPS;
11561   }
11562 }
11563
11564 static void GameActions_CheckSaveEngineSnapshot(void)
11565 {
11566   if (!game.snapshot.save_snapshot)
11567     return;
11568
11569   // clear flag for saving snapshot _before_ saving snapshot
11570   game.snapshot.save_snapshot = FALSE;
11571
11572   SaveEngineSnapshotToList();
11573 }
11574
11575 void GameActions(void)
11576 {
11577   GameActionsExt();
11578
11579   GameActions_CheckSaveEngineSnapshot();
11580 }
11581
11582 void GameActions_EM_Main(void)
11583 {
11584   byte effective_action[MAX_PLAYERS];
11585   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11586   int i;
11587
11588   for (i = 0; i < MAX_PLAYERS; i++)
11589     effective_action[i] = stored_player[i].effective_action;
11590
11591   GameActions_EM(effective_action, warp_mode);
11592 }
11593
11594 void GameActions_SP_Main(void)
11595 {
11596   byte effective_action[MAX_PLAYERS];
11597   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11598   int i;
11599
11600   for (i = 0; i < MAX_PLAYERS; i++)
11601     effective_action[i] = stored_player[i].effective_action;
11602
11603   GameActions_SP(effective_action, warp_mode);
11604
11605   for (i = 0; i < MAX_PLAYERS; i++)
11606   {
11607     if (stored_player[i].force_dropping)
11608       stored_player[i].action |= KEY_BUTTON_DROP;
11609
11610     stored_player[i].force_dropping = FALSE;
11611   }
11612 }
11613
11614 void GameActions_MM_Main(void)
11615 {
11616   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11617
11618   GameActions_MM(local_player->effective_mouse_action, warp_mode);
11619 }
11620
11621 void GameActions_RND_Main(void)
11622 {
11623   GameActions_RND();
11624 }
11625
11626 void GameActions_RND(void)
11627 {
11628   int magic_wall_x = 0, magic_wall_y = 0;
11629   int i, x, y, element, graphic, last_gfx_frame;
11630
11631   InitPlayfieldScanModeVars();
11632
11633   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11634   {
11635     SCAN_PLAYFIELD(x, y)
11636     {
11637       ChangeCount[x][y] = 0;
11638       ChangeEvent[x][y] = -1;
11639     }
11640   }
11641
11642   if (game.set_centered_player)
11643   {
11644     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11645
11646     // switching to "all players" only possible if all players fit to screen
11647     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11648     {
11649       game.centered_player_nr_next = game.centered_player_nr;
11650       game.set_centered_player = FALSE;
11651     }
11652
11653     // do not switch focus to non-existing (or non-active) player
11654     if (game.centered_player_nr_next >= 0 &&
11655         !stored_player[game.centered_player_nr_next].active)
11656     {
11657       game.centered_player_nr_next = game.centered_player_nr;
11658       game.set_centered_player = FALSE;
11659     }
11660   }
11661
11662   if (game.set_centered_player &&
11663       ScreenMovPos == 0)        // screen currently aligned at tile position
11664   {
11665     int sx, sy;
11666
11667     if (game.centered_player_nr_next == -1)
11668     {
11669       setScreenCenteredToAllPlayers(&sx, &sy);
11670     }
11671     else
11672     {
11673       sx = stored_player[game.centered_player_nr_next].jx;
11674       sy = stored_player[game.centered_player_nr_next].jy;
11675     }
11676
11677     game.centered_player_nr = game.centered_player_nr_next;
11678     game.set_centered_player = FALSE;
11679
11680     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11681     DrawGameDoorValues();
11682   }
11683
11684   for (i = 0; i < MAX_PLAYERS; i++)
11685   {
11686     int actual_player_action = stored_player[i].effective_action;
11687
11688 #if 1
11689     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11690        - rnd_equinox_tetrachloride 048
11691        - rnd_equinox_tetrachloride_ii 096
11692        - rnd_emanuel_schmieg 002
11693        - doctor_sloan_ww 001, 020
11694     */
11695     if (stored_player[i].MovPos == 0)
11696       CheckGravityMovement(&stored_player[i]);
11697 #endif
11698
11699     // overwrite programmed action with tape action
11700     if (stored_player[i].programmed_action)
11701       actual_player_action = stored_player[i].programmed_action;
11702
11703     PlayerActions(&stored_player[i], actual_player_action);
11704
11705     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11706   }
11707
11708   ScrollScreen(NULL, SCROLL_GO_ON);
11709
11710   /* for backwards compatibility, the following code emulates a fixed bug that
11711      occured when pushing elements (causing elements that just made their last
11712      pushing step to already (if possible) make their first falling step in the
11713      same game frame, which is bad); this code is also needed to use the famous
11714      "spring push bug" which is used in older levels and might be wanted to be
11715      used also in newer levels, but in this case the buggy pushing code is only
11716      affecting the "spring" element and no other elements */
11717
11718   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11719   {
11720     for (i = 0; i < MAX_PLAYERS; i++)
11721     {
11722       struct PlayerInfo *player = &stored_player[i];
11723       int x = player->jx;
11724       int y = player->jy;
11725
11726       if (player->active && player->is_pushing && player->is_moving &&
11727           IS_MOVING(x, y) &&
11728           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11729            Feld[x][y] == EL_SPRING))
11730       {
11731         ContinueMoving(x, y);
11732
11733         // continue moving after pushing (this is actually a bug)
11734         if (!IS_MOVING(x, y))
11735           Stop[x][y] = FALSE;
11736       }
11737     }
11738   }
11739
11740   SCAN_PLAYFIELD(x, y)
11741   {
11742     Last[x][y] = Feld[x][y];
11743
11744     ChangeCount[x][y] = 0;
11745     ChangeEvent[x][y] = -1;
11746
11747     // this must be handled before main playfield loop
11748     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11749     {
11750       MovDelay[x][y]--;
11751       if (MovDelay[x][y] <= 0)
11752         RemoveField(x, y);
11753     }
11754
11755     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11756     {
11757       MovDelay[x][y]--;
11758       if (MovDelay[x][y] <= 0)
11759       {
11760         RemoveField(x, y);
11761         TEST_DrawLevelField(x, y);
11762
11763         TestIfElementTouchesCustomElement(x, y);        // for empty space
11764       }
11765     }
11766
11767 #if DEBUG
11768     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11769     {
11770       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11771       printf("GameActions(): This should never happen!\n");
11772
11773       ChangePage[x][y] = -1;
11774     }
11775 #endif
11776
11777     Stop[x][y] = FALSE;
11778     if (WasJustMoving[x][y] > 0)
11779       WasJustMoving[x][y]--;
11780     if (WasJustFalling[x][y] > 0)
11781       WasJustFalling[x][y]--;
11782     if (CheckCollision[x][y] > 0)
11783       CheckCollision[x][y]--;
11784     if (CheckImpact[x][y] > 0)
11785       CheckImpact[x][y]--;
11786
11787     GfxFrame[x][y]++;
11788
11789     /* reset finished pushing action (not done in ContinueMoving() to allow
11790        continuous pushing animation for elements with zero push delay) */
11791     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11792     {
11793       ResetGfxAnimation(x, y);
11794       TEST_DrawLevelField(x, y);
11795     }
11796
11797 #if DEBUG
11798     if (IS_BLOCKED(x, y))
11799     {
11800       int oldx, oldy;
11801
11802       Blocked2Moving(x, y, &oldx, &oldy);
11803       if (!IS_MOVING(oldx, oldy))
11804       {
11805         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11806         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11807         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11808         printf("GameActions(): This should never happen!\n");
11809       }
11810     }
11811 #endif
11812   }
11813
11814   SCAN_PLAYFIELD(x, y)
11815   {
11816     element = Feld[x][y];
11817     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11818     last_gfx_frame = GfxFrame[x][y];
11819
11820     ResetGfxFrame(x, y);
11821
11822     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
11823       DrawLevelGraphicAnimation(x, y, graphic);
11824
11825     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11826         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11827       ResetRandomAnimationValue(x, y);
11828
11829     SetRandomAnimationValue(x, y);
11830
11831     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11832
11833     if (IS_INACTIVE(element))
11834     {
11835       if (IS_ANIMATED(graphic))
11836         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11837
11838       continue;
11839     }
11840
11841     // this may take place after moving, so 'element' may have changed
11842     if (IS_CHANGING(x, y) &&
11843         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11844     {
11845       int page = element_info[element].event_page_nr[CE_DELAY];
11846
11847       HandleElementChange(x, y, page);
11848
11849       element = Feld[x][y];
11850       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11851     }
11852
11853     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11854     {
11855       StartMoving(x, y);
11856
11857       element = Feld[x][y];
11858       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11859
11860       if (IS_ANIMATED(graphic) &&
11861           !IS_MOVING(x, y) &&
11862           !Stop[x][y])
11863         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11864
11865       if (IS_GEM(element) || element == EL_SP_INFOTRON)
11866         TEST_DrawTwinkleOnField(x, y);
11867     }
11868     else if (element == EL_ACID)
11869     {
11870       if (!Stop[x][y])
11871         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11872     }
11873     else if ((element == EL_EXIT_OPEN ||
11874               element == EL_EM_EXIT_OPEN ||
11875               element == EL_SP_EXIT_OPEN ||
11876               element == EL_STEEL_EXIT_OPEN ||
11877               element == EL_EM_STEEL_EXIT_OPEN ||
11878               element == EL_SP_TERMINAL ||
11879               element == EL_SP_TERMINAL_ACTIVE ||
11880               element == EL_EXTRA_TIME ||
11881               element == EL_SHIELD_NORMAL ||
11882               element == EL_SHIELD_DEADLY) &&
11883              IS_ANIMATED(graphic))
11884       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11885     else if (IS_MOVING(x, y))
11886       ContinueMoving(x, y);
11887     else if (IS_ACTIVE_BOMB(element))
11888       CheckDynamite(x, y);
11889     else if (element == EL_AMOEBA_GROWING)
11890       AmoebeWaechst(x, y);
11891     else if (element == EL_AMOEBA_SHRINKING)
11892       AmoebaDisappearing(x, y);
11893
11894 #if !USE_NEW_AMOEBA_CODE
11895     else if (IS_AMOEBALIVE(element))
11896       AmoebeAbleger(x, y);
11897 #endif
11898
11899     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11900       Life(x, y);
11901     else if (element == EL_EXIT_CLOSED)
11902       CheckExit(x, y);
11903     else if (element == EL_EM_EXIT_CLOSED)
11904       CheckExitEM(x, y);
11905     else if (element == EL_STEEL_EXIT_CLOSED)
11906       CheckExitSteel(x, y);
11907     else if (element == EL_EM_STEEL_EXIT_CLOSED)
11908       CheckExitSteelEM(x, y);
11909     else if (element == EL_SP_EXIT_CLOSED)
11910       CheckExitSP(x, y);
11911     else if (element == EL_EXPANDABLE_WALL_GROWING ||
11912              element == EL_EXPANDABLE_STEELWALL_GROWING)
11913       MauerWaechst(x, y);
11914     else if (element == EL_EXPANDABLE_WALL ||
11915              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11916              element == EL_EXPANDABLE_WALL_VERTICAL ||
11917              element == EL_EXPANDABLE_WALL_ANY ||
11918              element == EL_BD_EXPANDABLE_WALL)
11919       MauerAbleger(x, y);
11920     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11921              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11922              element == EL_EXPANDABLE_STEELWALL_ANY)
11923       MauerAblegerStahl(x, y);
11924     else if (element == EL_FLAMES)
11925       CheckForDragon(x, y);
11926     else if (element == EL_EXPLOSION)
11927       ; // drawing of correct explosion animation is handled separately
11928     else if (element == EL_ELEMENT_SNAPPING ||
11929              element == EL_DIAGONAL_SHRINKING ||
11930              element == EL_DIAGONAL_GROWING)
11931     {
11932       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11933
11934       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11935     }
11936     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11937       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11938
11939     if (IS_BELT_ACTIVE(element))
11940       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11941
11942     if (game.magic_wall_active)
11943     {
11944       int jx = local_player->jx, jy = local_player->jy;
11945
11946       // play the element sound at the position nearest to the player
11947       if ((element == EL_MAGIC_WALL_FULL ||
11948            element == EL_MAGIC_WALL_ACTIVE ||
11949            element == EL_MAGIC_WALL_EMPTYING ||
11950            element == EL_BD_MAGIC_WALL_FULL ||
11951            element == EL_BD_MAGIC_WALL_ACTIVE ||
11952            element == EL_BD_MAGIC_WALL_EMPTYING ||
11953            element == EL_DC_MAGIC_WALL_FULL ||
11954            element == EL_DC_MAGIC_WALL_ACTIVE ||
11955            element == EL_DC_MAGIC_WALL_EMPTYING) &&
11956           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11957       {
11958         magic_wall_x = x;
11959         magic_wall_y = y;
11960       }
11961     }
11962   }
11963
11964 #if USE_NEW_AMOEBA_CODE
11965   // new experimental amoeba growth stuff
11966   if (!(FrameCounter % 8))
11967   {
11968     static unsigned int random = 1684108901;
11969
11970     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11971     {
11972       x = RND(lev_fieldx);
11973       y = RND(lev_fieldy);
11974       element = Feld[x][y];
11975
11976       if (!IS_PLAYER(x,y) &&
11977           (element == EL_EMPTY ||
11978            CAN_GROW_INTO(element) ||
11979            element == EL_QUICKSAND_EMPTY ||
11980            element == EL_QUICKSAND_FAST_EMPTY ||
11981            element == EL_ACID_SPLASH_LEFT ||
11982            element == EL_ACID_SPLASH_RIGHT))
11983       {
11984         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
11985             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
11986             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
11987             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
11988           Feld[x][y] = EL_AMOEBA_DROP;
11989       }
11990
11991       random = random * 129 + 1;
11992     }
11993   }
11994 #endif
11995
11996   game.explosions_delayed = FALSE;
11997
11998   SCAN_PLAYFIELD(x, y)
11999   {
12000     element = Feld[x][y];
12001
12002     if (ExplodeField[x][y])
12003       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12004     else if (element == EL_EXPLOSION)
12005       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12006
12007     ExplodeField[x][y] = EX_TYPE_NONE;
12008   }
12009
12010   game.explosions_delayed = TRUE;
12011
12012   if (game.magic_wall_active)
12013   {
12014     if (!(game.magic_wall_time_left % 4))
12015     {
12016       int element = Feld[magic_wall_x][magic_wall_y];
12017
12018       if (element == EL_BD_MAGIC_WALL_FULL ||
12019           element == EL_BD_MAGIC_WALL_ACTIVE ||
12020           element == EL_BD_MAGIC_WALL_EMPTYING)
12021         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12022       else if (element == EL_DC_MAGIC_WALL_FULL ||
12023                element == EL_DC_MAGIC_WALL_ACTIVE ||
12024                element == EL_DC_MAGIC_WALL_EMPTYING)
12025         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12026       else
12027         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12028     }
12029
12030     if (game.magic_wall_time_left > 0)
12031     {
12032       game.magic_wall_time_left--;
12033
12034       if (!game.magic_wall_time_left)
12035       {
12036         SCAN_PLAYFIELD(x, y)
12037         {
12038           element = Feld[x][y];
12039
12040           if (element == EL_MAGIC_WALL_ACTIVE ||
12041               element == EL_MAGIC_WALL_FULL)
12042           {
12043             Feld[x][y] = EL_MAGIC_WALL_DEAD;
12044             TEST_DrawLevelField(x, y);
12045           }
12046           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12047                    element == EL_BD_MAGIC_WALL_FULL)
12048           {
12049             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
12050             TEST_DrawLevelField(x, y);
12051           }
12052           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12053                    element == EL_DC_MAGIC_WALL_FULL)
12054           {
12055             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
12056             TEST_DrawLevelField(x, y);
12057           }
12058         }
12059
12060         game.magic_wall_active = FALSE;
12061       }
12062     }
12063   }
12064
12065   if (game.light_time_left > 0)
12066   {
12067     game.light_time_left--;
12068
12069     if (game.light_time_left == 0)
12070       RedrawAllLightSwitchesAndInvisibleElements();
12071   }
12072
12073   if (game.timegate_time_left > 0)
12074   {
12075     game.timegate_time_left--;
12076
12077     if (game.timegate_time_left == 0)
12078       CloseAllOpenTimegates();
12079   }
12080
12081   if (game.lenses_time_left > 0)
12082   {
12083     game.lenses_time_left--;
12084
12085     if (game.lenses_time_left == 0)
12086       RedrawAllInvisibleElementsForLenses();
12087   }
12088
12089   if (game.magnify_time_left > 0)
12090   {
12091     game.magnify_time_left--;
12092
12093     if (game.magnify_time_left == 0)
12094       RedrawAllInvisibleElementsForMagnifier();
12095   }
12096
12097   for (i = 0; i < MAX_PLAYERS; i++)
12098   {
12099     struct PlayerInfo *player = &stored_player[i];
12100
12101     if (SHIELD_ON(player))
12102     {
12103       if (player->shield_deadly_time_left)
12104         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12105       else if (player->shield_normal_time_left)
12106         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12107     }
12108   }
12109
12110 #if USE_DELAYED_GFX_REDRAW
12111   SCAN_PLAYFIELD(x, y)
12112   {
12113     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12114     {
12115       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12116          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12117
12118       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12119         DrawLevelField(x, y);
12120
12121       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12122         DrawLevelFieldCrumbled(x, y);
12123
12124       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12125         DrawLevelFieldCrumbledNeighbours(x, y);
12126
12127       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12128         DrawTwinkleOnField(x, y);
12129     }
12130
12131     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12132   }
12133 #endif
12134
12135   DrawAllPlayers();
12136   PlayAllPlayersSound();
12137
12138   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
12139   {
12140     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
12141
12142     local_player->show_envelope = 0;
12143   }
12144
12145   // use random number generator in every frame to make it less predictable
12146   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12147     RND(1);
12148 }
12149
12150 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12151 {
12152   int min_x = x, min_y = y, max_x = x, max_y = y;
12153   int i;
12154
12155   for (i = 0; i < MAX_PLAYERS; i++)
12156   {
12157     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12158
12159     if (!stored_player[i].active || &stored_player[i] == player)
12160       continue;
12161
12162     min_x = MIN(min_x, jx);
12163     min_y = MIN(min_y, jy);
12164     max_x = MAX(max_x, jx);
12165     max_y = MAX(max_y, jy);
12166   }
12167
12168   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12169 }
12170
12171 static boolean AllPlayersInVisibleScreen(void)
12172 {
12173   int i;
12174
12175   for (i = 0; i < MAX_PLAYERS; i++)
12176   {
12177     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12178
12179     if (!stored_player[i].active)
12180       continue;
12181
12182     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12183       return FALSE;
12184   }
12185
12186   return TRUE;
12187 }
12188
12189 void ScrollLevel(int dx, int dy)
12190 {
12191   int scroll_offset = 2 * TILEX_VAR;
12192   int x, y;
12193
12194   BlitBitmap(drawto_field, drawto_field,
12195              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12196              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12197              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12198              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12199              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12200              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12201
12202   if (dx != 0)
12203   {
12204     x = (dx == 1 ? BX1 : BX2);
12205     for (y = BY1; y <= BY2; y++)
12206       DrawScreenField(x, y);
12207   }
12208
12209   if (dy != 0)
12210   {
12211     y = (dy == 1 ? BY1 : BY2);
12212     for (x = BX1; x <= BX2; x++)
12213       DrawScreenField(x, y);
12214   }
12215
12216   redraw_mask |= REDRAW_FIELD;
12217 }
12218
12219 static boolean canFallDown(struct PlayerInfo *player)
12220 {
12221   int jx = player->jx, jy = player->jy;
12222
12223   return (IN_LEV_FIELD(jx, jy + 1) &&
12224           (IS_FREE(jx, jy + 1) ||
12225            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12226           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
12227           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
12228 }
12229
12230 static boolean canPassField(int x, int y, int move_dir)
12231 {
12232   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12233   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12234   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12235   int nextx = x + dx;
12236   int nexty = y + dy;
12237   int element = Feld[x][y];
12238
12239   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12240           !CAN_MOVE(element) &&
12241           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12242           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
12243           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12244 }
12245
12246 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12247 {
12248   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12249   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12250   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12251   int newx = x + dx;
12252   int newy = y + dy;
12253
12254   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12255           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12256           (IS_DIGGABLE(Feld[newx][newy]) ||
12257            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12258            canPassField(newx, newy, move_dir)));
12259 }
12260
12261 static void CheckGravityMovement(struct PlayerInfo *player)
12262 {
12263   if (player->gravity && !player->programmed_action)
12264   {
12265     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12266     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12267     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12268     int jx = player->jx, jy = player->jy;
12269     boolean player_is_moving_to_valid_field =
12270       (!player_is_snapping &&
12271        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12272         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12273     boolean player_can_fall_down = canFallDown(player);
12274
12275     if (player_can_fall_down &&
12276         !player_is_moving_to_valid_field)
12277       player->programmed_action = MV_DOWN;
12278   }
12279 }
12280
12281 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12282 {
12283   return CheckGravityMovement(player);
12284
12285   if (player->gravity && !player->programmed_action)
12286   {
12287     int jx = player->jx, jy = player->jy;
12288     boolean field_under_player_is_free =
12289       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12290     boolean player_is_standing_on_valid_field =
12291       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12292        (IS_WALKABLE(Feld[jx][jy]) &&
12293         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12294
12295     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12296       player->programmed_action = MV_DOWN;
12297   }
12298 }
12299
12300 /*
12301   MovePlayerOneStep()
12302   -----------------------------------------------------------------------------
12303   dx, dy:               direction (non-diagonal) to try to move the player to
12304   real_dx, real_dy:     direction as read from input device (can be diagonal)
12305 */
12306
12307 boolean MovePlayerOneStep(struct PlayerInfo *player,
12308                           int dx, int dy, int real_dx, int real_dy)
12309 {
12310   int jx = player->jx, jy = player->jy;
12311   int new_jx = jx + dx, new_jy = jy + dy;
12312   int can_move;
12313   boolean player_can_move = !player->cannot_move;
12314
12315   if (!player->active || (!dx && !dy))
12316     return MP_NO_ACTION;
12317
12318   player->MovDir = (dx < 0 ? MV_LEFT :
12319                     dx > 0 ? MV_RIGHT :
12320                     dy < 0 ? MV_UP :
12321                     dy > 0 ? MV_DOWN :  MV_NONE);
12322
12323   if (!IN_LEV_FIELD(new_jx, new_jy))
12324     return MP_NO_ACTION;
12325
12326   if (!player_can_move)
12327   {
12328     if (player->MovPos == 0)
12329     {
12330       player->is_moving = FALSE;
12331       player->is_digging = FALSE;
12332       player->is_collecting = FALSE;
12333       player->is_snapping = FALSE;
12334       player->is_pushing = FALSE;
12335     }
12336   }
12337
12338   if (!network.enabled && game.centered_player_nr == -1 &&
12339       !AllPlayersInSight(player, new_jx, new_jy))
12340     return MP_NO_ACTION;
12341
12342   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12343   if (can_move != MP_MOVING)
12344     return can_move;
12345
12346   // check if DigField() has caused relocation of the player
12347   if (player->jx != jx || player->jy != jy)
12348     return MP_NO_ACTION;        // <-- !!! CHECK THIS [-> MP_ACTION ?] !!!
12349
12350   StorePlayer[jx][jy] = 0;
12351   player->last_jx = jx;
12352   player->last_jy = jy;
12353   player->jx = new_jx;
12354   player->jy = new_jy;
12355   StorePlayer[new_jx][new_jy] = player->element_nr;
12356
12357   if (player->move_delay_value_next != -1)
12358   {
12359     player->move_delay_value = player->move_delay_value_next;
12360     player->move_delay_value_next = -1;
12361   }
12362
12363   player->MovPos =
12364     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12365
12366   player->step_counter++;
12367
12368   PlayerVisit[jx][jy] = FrameCounter;
12369
12370   player->is_moving = TRUE;
12371
12372 #if 1
12373   // should better be called in MovePlayer(), but this breaks some tapes
12374   ScrollPlayer(player, SCROLL_INIT);
12375 #endif
12376
12377   return MP_MOVING;
12378 }
12379
12380 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12381 {
12382   int jx = player->jx, jy = player->jy;
12383   int old_jx = jx, old_jy = jy;
12384   int moved = MP_NO_ACTION;
12385
12386   if (!player->active)
12387     return FALSE;
12388
12389   if (!dx && !dy)
12390   {
12391     if (player->MovPos == 0)
12392     {
12393       player->is_moving = FALSE;
12394       player->is_digging = FALSE;
12395       player->is_collecting = FALSE;
12396       player->is_snapping = FALSE;
12397       player->is_pushing = FALSE;
12398     }
12399
12400     return FALSE;
12401   }
12402
12403   if (player->move_delay > 0)
12404     return FALSE;
12405
12406   player->move_delay = -1;              // set to "uninitialized" value
12407
12408   // store if player is automatically moved to next field
12409   player->is_auto_moving = (player->programmed_action != MV_NONE);
12410
12411   // remove the last programmed player action
12412   player->programmed_action = 0;
12413
12414   if (player->MovPos)
12415   {
12416     // should only happen if pre-1.2 tape recordings are played
12417     // this is only for backward compatibility
12418
12419     int original_move_delay_value = player->move_delay_value;
12420
12421 #if DEBUG
12422     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
12423            tape.counter);
12424 #endif
12425
12426     // scroll remaining steps with finest movement resolution
12427     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12428
12429     while (player->MovPos)
12430     {
12431       ScrollPlayer(player, SCROLL_GO_ON);
12432       ScrollScreen(NULL, SCROLL_GO_ON);
12433
12434       AdvanceFrameAndPlayerCounters(player->index_nr);
12435
12436       DrawAllPlayers();
12437       BackToFront_WithFrameDelay(0);
12438     }
12439
12440     player->move_delay_value = original_move_delay_value;
12441   }
12442
12443   player->is_active = FALSE;
12444
12445   if (player->last_move_dir & MV_HORIZONTAL)
12446   {
12447     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12448       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12449   }
12450   else
12451   {
12452     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12453       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12454   }
12455
12456   if (!moved && !player->is_active)
12457   {
12458     player->is_moving = FALSE;
12459     player->is_digging = FALSE;
12460     player->is_collecting = FALSE;
12461     player->is_snapping = FALSE;
12462     player->is_pushing = FALSE;
12463   }
12464
12465   jx = player->jx;
12466   jy = player->jy;
12467
12468   if (moved & MP_MOVING && !ScreenMovPos &&
12469       (player->index_nr == game.centered_player_nr ||
12470        game.centered_player_nr == -1))
12471   {
12472     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12473     int offset = game.scroll_delay_value;
12474
12475     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12476     {
12477       // actual player has left the screen -- scroll in that direction
12478       if (jx != old_jx)         // player has moved horizontally
12479         scroll_x += (jx - old_jx);
12480       else                      // player has moved vertically
12481         scroll_y += (jy - old_jy);
12482     }
12483     else
12484     {
12485       if (jx != old_jx)         // player has moved horizontally
12486       {
12487         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
12488             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12489           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12490
12491         // don't scroll over playfield boundaries
12492         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12493           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12494
12495         // don't scroll more than one field at a time
12496         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12497
12498         // don't scroll against the player's moving direction
12499         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12500             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12501           scroll_x = old_scroll_x;
12502       }
12503       else                      // player has moved vertically
12504       {
12505         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
12506             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12507           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12508
12509         // don't scroll over playfield boundaries
12510         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12511           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12512
12513         // don't scroll more than one field at a time
12514         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12515
12516         // don't scroll against the player's moving direction
12517         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12518             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12519           scroll_y = old_scroll_y;
12520       }
12521     }
12522
12523     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12524     {
12525       if (!network.enabled && game.centered_player_nr == -1 &&
12526           !AllPlayersInVisibleScreen())
12527       {
12528         scroll_x = old_scroll_x;
12529         scroll_y = old_scroll_y;
12530       }
12531       else
12532       {
12533         ScrollScreen(player, SCROLL_INIT);
12534         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12535       }
12536     }
12537   }
12538
12539   player->StepFrame = 0;
12540
12541   if (moved & MP_MOVING)
12542   {
12543     if (old_jx != jx && old_jy == jy)
12544       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12545     else if (old_jx == jx && old_jy != jy)
12546       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12547
12548     TEST_DrawLevelField(jx, jy);        // for "crumbled sand"
12549
12550     player->last_move_dir = player->MovDir;
12551     player->is_moving = TRUE;
12552     player->is_snapping = FALSE;
12553     player->is_switching = FALSE;
12554     player->is_dropping = FALSE;
12555     player->is_dropping_pressed = FALSE;
12556     player->drop_pressed_delay = 0;
12557
12558 #if 0
12559     // should better be called here than above, but this breaks some tapes
12560     ScrollPlayer(player, SCROLL_INIT);
12561 #endif
12562   }
12563   else
12564   {
12565     CheckGravityMovementWhenNotMoving(player);
12566
12567     player->is_moving = FALSE;
12568
12569     /* at this point, the player is allowed to move, but cannot move right now
12570        (e.g. because of something blocking the way) -- ensure that the player
12571        is also allowed to move in the next frame (in old versions before 3.1.1,
12572        the player was forced to wait again for eight frames before next try) */
12573
12574     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12575       player->move_delay = 0;   // allow direct movement in the next frame
12576   }
12577
12578   if (player->move_delay == -1)         // not yet initialized by DigField()
12579     player->move_delay = player->move_delay_value;
12580
12581   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12582   {
12583     TestIfPlayerTouchesBadThing(jx, jy);
12584     TestIfPlayerTouchesCustomElement(jx, jy);
12585   }
12586
12587   if (!player->active)
12588     RemovePlayer(player);
12589
12590   return moved;
12591 }
12592
12593 void ScrollPlayer(struct PlayerInfo *player, int mode)
12594 {
12595   int jx = player->jx, jy = player->jy;
12596   int last_jx = player->last_jx, last_jy = player->last_jy;
12597   int move_stepsize = TILEX / player->move_delay_value;
12598
12599   if (!player->active)
12600     return;
12601
12602   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      // player not moving
12603     return;
12604
12605   if (mode == SCROLL_INIT)
12606   {
12607     player->actual_frame_counter = FrameCounter;
12608     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12609
12610     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12611         Feld[last_jx][last_jy] == EL_EMPTY)
12612     {
12613       int last_field_block_delay = 0;   // start with no blocking at all
12614       int block_delay_adjustment = player->block_delay_adjustment;
12615
12616       // if player blocks last field, add delay for exactly one move
12617       if (player->block_last_field)
12618       {
12619         last_field_block_delay += player->move_delay_value;
12620
12621         // when blocking enabled, prevent moving up despite gravity
12622         if (player->gravity && player->MovDir == MV_UP)
12623           block_delay_adjustment = -1;
12624       }
12625
12626       // add block delay adjustment (also possible when not blocking)
12627       last_field_block_delay += block_delay_adjustment;
12628
12629       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12630       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12631     }
12632
12633     if (player->MovPos != 0)    // player has not yet reached destination
12634       return;
12635   }
12636   else if (!FrameReached(&player->actual_frame_counter, 1))
12637     return;
12638
12639   if (player->MovPos != 0)
12640   {
12641     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12642     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12643
12644     // before DrawPlayer() to draw correct player graphic for this case
12645     if (player->MovPos == 0)
12646       CheckGravityMovement(player);
12647   }
12648
12649   if (player->MovPos == 0)      // player reached destination field
12650   {
12651     if (player->move_delay_reset_counter > 0)
12652     {
12653       player->move_delay_reset_counter--;
12654
12655       if (player->move_delay_reset_counter == 0)
12656       {
12657         // continue with normal speed after quickly moving through gate
12658         HALVE_PLAYER_SPEED(player);
12659
12660         // be able to make the next move without delay
12661         player->move_delay = 0;
12662       }
12663     }
12664
12665     player->last_jx = jx;
12666     player->last_jy = jy;
12667
12668     if (Feld[jx][jy] == EL_EXIT_OPEN ||
12669         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12670         Feld[jx][jy] == EL_EM_EXIT_OPENING ||
12671         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12672         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12673         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12674         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12675         Feld[jx][jy] == EL_SP_EXIT_OPENING)     // <-- special case
12676     {
12677       ExitPlayer(player);
12678
12679       if ((local_player->friends_still_needed == 0 ||
12680            IS_SP_ELEMENT(Feld[jx][jy])) &&
12681           AllPlayersGone)
12682         PlayerWins(local_player);
12683     }
12684
12685     // this breaks one level: "machine", level 000
12686     {
12687       int move_direction = player->MovDir;
12688       int enter_side = MV_DIR_OPPOSITE(move_direction);
12689       int leave_side = move_direction;
12690       int old_jx = last_jx;
12691       int old_jy = last_jy;
12692       int old_element = Feld[old_jx][old_jy];
12693       int new_element = Feld[jx][jy];
12694
12695       if (IS_CUSTOM_ELEMENT(old_element))
12696         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12697                                    CE_LEFT_BY_PLAYER,
12698                                    player->index_bit, leave_side);
12699
12700       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12701                                           CE_PLAYER_LEAVES_X,
12702                                           player->index_bit, leave_side);
12703
12704       if (IS_CUSTOM_ELEMENT(new_element))
12705         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12706                                    player->index_bit, enter_side);
12707
12708       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12709                                           CE_PLAYER_ENTERS_X,
12710                                           player->index_bit, enter_side);
12711
12712       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12713                                         CE_MOVE_OF_X, move_direction);
12714     }
12715
12716     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12717     {
12718       TestIfPlayerTouchesBadThing(jx, jy);
12719       TestIfPlayerTouchesCustomElement(jx, jy);
12720
12721       /* needed because pushed element has not yet reached its destination,
12722          so it would trigger a change event at its previous field location */
12723       if (!player->is_pushing)
12724         TestIfElementTouchesCustomElement(jx, jy);      // for empty space
12725
12726       if (!player->active)
12727         RemovePlayer(player);
12728     }
12729
12730     if (!local_player->LevelSolved && level.use_step_counter)
12731     {
12732       int i;
12733
12734       TimePlayed++;
12735
12736       if (TimeLeft > 0)
12737       {
12738         TimeLeft--;
12739
12740         if (TimeLeft <= 10 && setup.time_limit)
12741           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12742
12743         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12744
12745         DisplayGameControlValues();
12746
12747         if (!TimeLeft && setup.time_limit)
12748           for (i = 0; i < MAX_PLAYERS; i++)
12749             KillPlayer(&stored_player[i]);
12750       }
12751       else if (game.no_time_limit && !AllPlayersGone) // level w/o time limit
12752       {
12753         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12754
12755         DisplayGameControlValues();
12756       }
12757     }
12758
12759     if (tape.single_step && tape.recording && !tape.pausing &&
12760         !player->programmed_action)
12761       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12762
12763     if (!player->programmed_action)
12764       CheckSaveEngineSnapshot(player);
12765   }
12766 }
12767
12768 void ScrollScreen(struct PlayerInfo *player, int mode)
12769 {
12770   static unsigned int screen_frame_counter = 0;
12771
12772   if (mode == SCROLL_INIT)
12773   {
12774     // set scrolling step size according to actual player's moving speed
12775     ScrollStepSize = TILEX / player->move_delay_value;
12776
12777     screen_frame_counter = FrameCounter;
12778     ScreenMovDir = player->MovDir;
12779     ScreenMovPos = player->MovPos;
12780     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12781     return;
12782   }
12783   else if (!FrameReached(&screen_frame_counter, 1))
12784     return;
12785
12786   if (ScreenMovPos)
12787   {
12788     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12789     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12790     redraw_mask |= REDRAW_FIELD;
12791   }
12792   else
12793     ScreenMovDir = MV_NONE;
12794 }
12795
12796 void TestIfPlayerTouchesCustomElement(int x, int y)
12797 {
12798   static int xy[4][2] =
12799   {
12800     { 0, -1 },
12801     { -1, 0 },
12802     { +1, 0 },
12803     { 0, +1 }
12804   };
12805   static int trigger_sides[4][2] =
12806   {
12807     // center side       border side
12808     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
12809     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
12810     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
12811     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
12812   };
12813   static int touch_dir[4] =
12814   {
12815     MV_LEFT | MV_RIGHT,
12816     MV_UP   | MV_DOWN,
12817     MV_UP   | MV_DOWN,
12818     MV_LEFT | MV_RIGHT
12819   };
12820   int center_element = Feld[x][y];      // should always be non-moving!
12821   int i;
12822
12823   for (i = 0; i < NUM_DIRECTIONS; i++)
12824   {
12825     int xx = x + xy[i][0];
12826     int yy = y + xy[i][1];
12827     int center_side = trigger_sides[i][0];
12828     int border_side = trigger_sides[i][1];
12829     int border_element;
12830
12831     if (!IN_LEV_FIELD(xx, yy))
12832       continue;
12833
12834     if (IS_PLAYER(x, y))                // player found at center element
12835     {
12836       struct PlayerInfo *player = PLAYERINFO(x, y);
12837
12838       if (game.engine_version < VERSION_IDENT(3,0,7,0))
12839         border_element = Feld[xx][yy];          // may be moving!
12840       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12841         border_element = Feld[xx][yy];
12842       else if (MovDir[xx][yy] & touch_dir[i])   // elements are touching
12843         border_element = MovingOrBlocked2Element(xx, yy);
12844       else
12845         continue;               // center and border element do not touch
12846
12847       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12848                                  player->index_bit, border_side);
12849       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12850                                           CE_PLAYER_TOUCHES_X,
12851                                           player->index_bit, border_side);
12852
12853       {
12854         /* use player element that is initially defined in the level playfield,
12855            not the player element that corresponds to the runtime player number
12856            (example: a level that contains EL_PLAYER_3 as the only player would
12857            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12858         int player_element = PLAYERINFO(x, y)->initial_element;
12859
12860         CheckElementChangeBySide(xx, yy, border_element, player_element,
12861                                  CE_TOUCHING_X, border_side);
12862       }
12863     }
12864     else if (IS_PLAYER(xx, yy))         // player found at border element
12865     {
12866       struct PlayerInfo *player = PLAYERINFO(xx, yy);
12867
12868       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12869       {
12870         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12871           continue;             // center and border element do not touch
12872       }
12873
12874       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12875                                  player->index_bit, center_side);
12876       CheckTriggeredElementChangeByPlayer(x, y, center_element,
12877                                           CE_PLAYER_TOUCHES_X,
12878                                           player->index_bit, center_side);
12879
12880       {
12881         /* use player element that is initially defined in the level playfield,
12882            not the player element that corresponds to the runtime player number
12883            (example: a level that contains EL_PLAYER_3 as the only player would
12884            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12885         int player_element = PLAYERINFO(xx, yy)->initial_element;
12886
12887         CheckElementChangeBySide(x, y, center_element, player_element,
12888                                  CE_TOUCHING_X, center_side);
12889       }
12890
12891       break;
12892     }
12893   }
12894 }
12895
12896 void TestIfElementTouchesCustomElement(int x, int y)
12897 {
12898   static int xy[4][2] =
12899   {
12900     { 0, -1 },
12901     { -1, 0 },
12902     { +1, 0 },
12903     { 0, +1 }
12904   };
12905   static int trigger_sides[4][2] =
12906   {
12907     // center side      border side
12908     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
12909     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
12910     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
12911     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
12912   };
12913   static int touch_dir[4] =
12914   {
12915     MV_LEFT | MV_RIGHT,
12916     MV_UP   | MV_DOWN,
12917     MV_UP   | MV_DOWN,
12918     MV_LEFT | MV_RIGHT
12919   };
12920   boolean change_center_element = FALSE;
12921   int center_element = Feld[x][y];      // should always be non-moving!
12922   int border_element_old[NUM_DIRECTIONS];
12923   int i;
12924
12925   for (i = 0; i < NUM_DIRECTIONS; i++)
12926   {
12927     int xx = x + xy[i][0];
12928     int yy = y + xy[i][1];
12929     int border_element;
12930
12931     border_element_old[i] = -1;
12932
12933     if (!IN_LEV_FIELD(xx, yy))
12934       continue;
12935
12936     if (game.engine_version < VERSION_IDENT(3,0,7,0))
12937       border_element = Feld[xx][yy];    // may be moving!
12938     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12939       border_element = Feld[xx][yy];
12940     else if (MovDir[xx][yy] & touch_dir[i])     // elements are touching
12941       border_element = MovingOrBlocked2Element(xx, yy);
12942     else
12943       continue;                 // center and border element do not touch
12944
12945     border_element_old[i] = border_element;
12946   }
12947
12948   for (i = 0; i < NUM_DIRECTIONS; i++)
12949   {
12950     int xx = x + xy[i][0];
12951     int yy = y + xy[i][1];
12952     int center_side = trigger_sides[i][0];
12953     int border_element = border_element_old[i];
12954
12955     if (border_element == -1)
12956       continue;
12957
12958     // check for change of border element
12959     CheckElementChangeBySide(xx, yy, border_element, center_element,
12960                              CE_TOUCHING_X, center_side);
12961
12962     // (center element cannot be player, so we dont have to check this here)
12963   }
12964
12965   for (i = 0; i < NUM_DIRECTIONS; i++)
12966   {
12967     int xx = x + xy[i][0];
12968     int yy = y + xy[i][1];
12969     int border_side = trigger_sides[i][1];
12970     int border_element = border_element_old[i];
12971
12972     if (border_element == -1)
12973       continue;
12974
12975     // check for change of center element (but change it only once)
12976     if (!change_center_element)
12977       change_center_element =
12978         CheckElementChangeBySide(x, y, center_element, border_element,
12979                                  CE_TOUCHING_X, border_side);
12980
12981     if (IS_PLAYER(xx, yy))
12982     {
12983       /* use player element that is initially defined in the level playfield,
12984          not the player element that corresponds to the runtime player number
12985          (example: a level that contains EL_PLAYER_3 as the only player would
12986          incorrectly give EL_PLAYER_1 for "player->element_nr") */
12987       int player_element = PLAYERINFO(xx, yy)->initial_element;
12988
12989       CheckElementChangeBySide(x, y, center_element, player_element,
12990                                CE_TOUCHING_X, border_side);
12991     }
12992   }
12993 }
12994
12995 void TestIfElementHitsCustomElement(int x, int y, int direction)
12996 {
12997   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12998   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
12999   int hitx = x + dx, hity = y + dy;
13000   int hitting_element = Feld[x][y];
13001   int touched_element;
13002
13003   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13004     return;
13005
13006   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13007                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13008
13009   if (IN_LEV_FIELD(hitx, hity))
13010   {
13011     int opposite_direction = MV_DIR_OPPOSITE(direction);
13012     int hitting_side = direction;
13013     int touched_side = opposite_direction;
13014     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13015                           MovDir[hitx][hity] != direction ||
13016                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13017
13018     object_hit = TRUE;
13019
13020     if (object_hit)
13021     {
13022       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13023                                CE_HITTING_X, touched_side);
13024
13025       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13026                                CE_HIT_BY_X, hitting_side);
13027
13028       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13029                                CE_HIT_BY_SOMETHING, opposite_direction);
13030
13031       if (IS_PLAYER(hitx, hity))
13032       {
13033         /* use player element that is initially defined in the level playfield,
13034            not the player element that corresponds to the runtime player number
13035            (example: a level that contains EL_PLAYER_3 as the only player would
13036            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13037         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13038
13039         CheckElementChangeBySide(x, y, hitting_element, player_element,
13040                                  CE_HITTING_X, touched_side);
13041       }
13042     }
13043   }
13044
13045   // "hitting something" is also true when hitting the playfield border
13046   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13047                            CE_HITTING_SOMETHING, direction);
13048 }
13049
13050 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13051 {
13052   int i, kill_x = -1, kill_y = -1;
13053
13054   int bad_element = -1;
13055   static int test_xy[4][2] =
13056   {
13057     { 0, -1 },
13058     { -1, 0 },
13059     { +1, 0 },
13060     { 0, +1 }
13061   };
13062   static int test_dir[4] =
13063   {
13064     MV_UP,
13065     MV_LEFT,
13066     MV_RIGHT,
13067     MV_DOWN
13068   };
13069
13070   for (i = 0; i < NUM_DIRECTIONS; i++)
13071   {
13072     int test_x, test_y, test_move_dir, test_element;
13073
13074     test_x = good_x + test_xy[i][0];
13075     test_y = good_y + test_xy[i][1];
13076
13077     if (!IN_LEV_FIELD(test_x, test_y))
13078       continue;
13079
13080     test_move_dir =
13081       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13082
13083     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13084
13085     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13086        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13087     */
13088     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13089         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13090     {
13091       kill_x = test_x;
13092       kill_y = test_y;
13093       bad_element = test_element;
13094
13095       break;
13096     }
13097   }
13098
13099   if (kill_x != -1 || kill_y != -1)
13100   {
13101     if (IS_PLAYER(good_x, good_y))
13102     {
13103       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13104
13105       if (player->shield_deadly_time_left > 0 &&
13106           !IS_INDESTRUCTIBLE(bad_element))
13107         Bang(kill_x, kill_y);
13108       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13109         KillPlayer(player);
13110     }
13111     else
13112       Bang(good_x, good_y);
13113   }
13114 }
13115
13116 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13117 {
13118   int i, kill_x = -1, kill_y = -1;
13119   int bad_element = Feld[bad_x][bad_y];
13120   static int test_xy[4][2] =
13121   {
13122     { 0, -1 },
13123     { -1, 0 },
13124     { +1, 0 },
13125     { 0, +1 }
13126   };
13127   static int touch_dir[4] =
13128   {
13129     MV_LEFT | MV_RIGHT,
13130     MV_UP   | MV_DOWN,
13131     MV_UP   | MV_DOWN,
13132     MV_LEFT | MV_RIGHT
13133   };
13134   static int test_dir[4] =
13135   {
13136     MV_UP,
13137     MV_LEFT,
13138     MV_RIGHT,
13139     MV_DOWN
13140   };
13141
13142   if (bad_element == EL_EXPLOSION)      // skip just exploding bad things
13143     return;
13144
13145   for (i = 0; i < NUM_DIRECTIONS; i++)
13146   {
13147     int test_x, test_y, test_move_dir, test_element;
13148
13149     test_x = bad_x + test_xy[i][0];
13150     test_y = bad_y + test_xy[i][1];
13151
13152     if (!IN_LEV_FIELD(test_x, test_y))
13153       continue;
13154
13155     test_move_dir =
13156       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13157
13158     test_element = Feld[test_x][test_y];
13159
13160     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13161        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13162     */
13163     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13164         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13165     {
13166       // good thing is player or penguin that does not move away
13167       if (IS_PLAYER(test_x, test_y))
13168       {
13169         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13170
13171         if (bad_element == EL_ROBOT && player->is_moving)
13172           continue;     // robot does not kill player if he is moving
13173
13174         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13175         {
13176           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13177             continue;           // center and border element do not touch
13178         }
13179
13180         kill_x = test_x;
13181         kill_y = test_y;
13182
13183         break;
13184       }
13185       else if (test_element == EL_PENGUIN)
13186       {
13187         kill_x = test_x;
13188         kill_y = test_y;
13189
13190         break;
13191       }
13192     }
13193   }
13194
13195   if (kill_x != -1 || kill_y != -1)
13196   {
13197     if (IS_PLAYER(kill_x, kill_y))
13198     {
13199       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13200
13201       if (player->shield_deadly_time_left > 0 &&
13202           !IS_INDESTRUCTIBLE(bad_element))
13203         Bang(bad_x, bad_y);
13204       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13205         KillPlayer(player);
13206     }
13207     else
13208       Bang(kill_x, kill_y);
13209   }
13210 }
13211
13212 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13213 {
13214   int bad_element = Feld[bad_x][bad_y];
13215   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13216   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13217   int test_x = bad_x + dx, test_y = bad_y + dy;
13218   int test_move_dir, test_element;
13219   int kill_x = -1, kill_y = -1;
13220
13221   if (!IN_LEV_FIELD(test_x, test_y))
13222     return;
13223
13224   test_move_dir =
13225     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13226
13227   test_element = Feld[test_x][test_y];
13228
13229   if (test_move_dir != bad_move_dir)
13230   {
13231     // good thing can be player or penguin that does not move away
13232     if (IS_PLAYER(test_x, test_y))
13233     {
13234       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13235
13236       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13237          player as being hit when he is moving towards the bad thing, because
13238          the "get hit by" condition would be lost after the player stops) */
13239       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13240         return;         // player moves away from bad thing
13241
13242       kill_x = test_x;
13243       kill_y = test_y;
13244     }
13245     else if (test_element == EL_PENGUIN)
13246     {
13247       kill_x = test_x;
13248       kill_y = test_y;
13249     }
13250   }
13251
13252   if (kill_x != -1 || kill_y != -1)
13253   {
13254     if (IS_PLAYER(kill_x, kill_y))
13255     {
13256       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13257
13258       if (player->shield_deadly_time_left > 0 &&
13259           !IS_INDESTRUCTIBLE(bad_element))
13260         Bang(bad_x, bad_y);
13261       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13262         KillPlayer(player);
13263     }
13264     else
13265       Bang(kill_x, kill_y);
13266   }
13267 }
13268
13269 void TestIfPlayerTouchesBadThing(int x, int y)
13270 {
13271   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13272 }
13273
13274 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13275 {
13276   TestIfGoodThingHitsBadThing(x, y, move_dir);
13277 }
13278
13279 void TestIfBadThingTouchesPlayer(int x, int y)
13280 {
13281   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13282 }
13283
13284 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13285 {
13286   TestIfBadThingHitsGoodThing(x, y, move_dir);
13287 }
13288
13289 void TestIfFriendTouchesBadThing(int x, int y)
13290 {
13291   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13292 }
13293
13294 void TestIfBadThingTouchesFriend(int x, int y)
13295 {
13296   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13297 }
13298
13299 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13300 {
13301   int i, kill_x = bad_x, kill_y = bad_y;
13302   static int xy[4][2] =
13303   {
13304     { 0, -1 },
13305     { -1, 0 },
13306     { +1, 0 },
13307     { 0, +1 }
13308   };
13309
13310   for (i = 0; i < NUM_DIRECTIONS; i++)
13311   {
13312     int x, y, element;
13313
13314     x = bad_x + xy[i][0];
13315     y = bad_y + xy[i][1];
13316     if (!IN_LEV_FIELD(x, y))
13317       continue;
13318
13319     element = Feld[x][y];
13320     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13321         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13322     {
13323       kill_x = x;
13324       kill_y = y;
13325       break;
13326     }
13327   }
13328
13329   if (kill_x != bad_x || kill_y != bad_y)
13330     Bang(bad_x, bad_y);
13331 }
13332
13333 void KillPlayer(struct PlayerInfo *player)
13334 {
13335   int jx = player->jx, jy = player->jy;
13336
13337   if (!player->active)
13338     return;
13339
13340 #if 0
13341   printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
13342          player->killed, player->active, player->reanimated);
13343 #endif
13344
13345   /* the following code was introduced to prevent an infinite loop when calling
13346      -> Bang()
13347      -> CheckTriggeredElementChangeExt()
13348      -> ExecuteCustomElementAction()
13349      -> KillPlayer()
13350      -> (infinitely repeating the above sequence of function calls)
13351      which occurs when killing the player while having a CE with the setting
13352      "kill player X when explosion of <player X>"; the solution using a new
13353      field "player->killed" was chosen for backwards compatibility, although
13354      clever use of the fields "player->active" etc. would probably also work */
13355 #if 1
13356   if (player->killed)
13357     return;
13358 #endif
13359
13360   player->killed = TRUE;
13361
13362   // remove accessible field at the player's position
13363   Feld[jx][jy] = EL_EMPTY;
13364
13365   // deactivate shield (else Bang()/Explode() would not work right)
13366   player->shield_normal_time_left = 0;
13367   player->shield_deadly_time_left = 0;
13368
13369 #if 0
13370   printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
13371          player->killed, player->active, player->reanimated);
13372 #endif
13373
13374   Bang(jx, jy);
13375
13376 #if 0
13377   printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
13378          player->killed, player->active, player->reanimated);
13379 #endif
13380
13381   if (player->reanimated)       // killed player may have been reanimated
13382     player->killed = player->reanimated = FALSE;
13383   else
13384     BuryPlayer(player);
13385 }
13386
13387 static void KillPlayerUnlessEnemyProtected(int x, int y)
13388 {
13389   if (!PLAYER_ENEMY_PROTECTED(x, y))
13390     KillPlayer(PLAYERINFO(x, y));
13391 }
13392
13393 static void KillPlayerUnlessExplosionProtected(int x, int y)
13394 {
13395   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13396     KillPlayer(PLAYERINFO(x, y));
13397 }
13398
13399 void BuryPlayer(struct PlayerInfo *player)
13400 {
13401   int jx = player->jx, jy = player->jy;
13402
13403   if (!player->active)
13404     return;
13405
13406   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13407   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13408
13409   player->GameOver = TRUE;
13410   RemovePlayer(player);
13411 }
13412
13413 void RemovePlayer(struct PlayerInfo *player)
13414 {
13415   int jx = player->jx, jy = player->jy;
13416   int i, found = FALSE;
13417
13418   player->present = FALSE;
13419   player->active = FALSE;
13420
13421   if (!ExplodeField[jx][jy])
13422     StorePlayer[jx][jy] = 0;
13423
13424   if (player->is_moving)
13425     TEST_DrawLevelField(player->last_jx, player->last_jy);
13426
13427   for (i = 0; i < MAX_PLAYERS; i++)
13428     if (stored_player[i].active)
13429       found = TRUE;
13430
13431   if (!found)
13432     AllPlayersGone = TRUE;
13433
13434   ExitX = ZX = jx;
13435   ExitY = ZY = jy;
13436 }
13437
13438 void ExitPlayer(struct PlayerInfo *player)
13439 {
13440   DrawPlayer(player);   // needed here only to cleanup last field
13441   RemovePlayer(player);
13442
13443   if (local_player->players_still_needed > 0)
13444     local_player->players_still_needed--;
13445
13446   // also set if some players not yet gone, but not needed to solve level
13447   if (local_player->players_still_needed == 0)
13448     AllPlayersGone = TRUE;
13449 }
13450
13451 static void setFieldForSnapping(int x, int y, int element, int direction)
13452 {
13453   struct ElementInfo *ei = &element_info[element];
13454   int direction_bit = MV_DIR_TO_BIT(direction);
13455   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13456   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13457                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13458
13459   Feld[x][y] = EL_ELEMENT_SNAPPING;
13460   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13461
13462   ResetGfxAnimation(x, y);
13463
13464   GfxElement[x][y] = element;
13465   GfxAction[x][y] = action;
13466   GfxDir[x][y] = direction;
13467   GfxFrame[x][y] = -1;
13468 }
13469
13470 /*
13471   =============================================================================
13472   checkDiagonalPushing()
13473   -----------------------------------------------------------------------------
13474   check if diagonal input device direction results in pushing of object
13475   (by checking if the alternative direction is walkable, diggable, ...)
13476   =============================================================================
13477 */
13478
13479 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13480                                     int x, int y, int real_dx, int real_dy)
13481 {
13482   int jx, jy, dx, dy, xx, yy;
13483
13484   if (real_dx == 0 || real_dy == 0)     // no diagonal direction => push
13485     return TRUE;
13486
13487   // diagonal direction: check alternative direction
13488   jx = player->jx;
13489   jy = player->jy;
13490   dx = x - jx;
13491   dy = y - jy;
13492   xx = jx + (dx == 0 ? real_dx : 0);
13493   yy = jy + (dy == 0 ? real_dy : 0);
13494
13495   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13496 }
13497
13498 /*
13499   =============================================================================
13500   DigField()
13501   -----------------------------------------------------------------------------
13502   x, y:                 field next to player (non-diagonal) to try to dig to
13503   real_dx, real_dy:     direction as read from input device (can be diagonal)
13504   =============================================================================
13505 */
13506
13507 static int DigField(struct PlayerInfo *player,
13508                     int oldx, int oldy, int x, int y,
13509                     int real_dx, int real_dy, int mode)
13510 {
13511   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13512   boolean player_was_pushing = player->is_pushing;
13513   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13514   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13515   int jx = oldx, jy = oldy;
13516   int dx = x - jx, dy = y - jy;
13517   int nextx = x + dx, nexty = y + dy;
13518   int move_direction = (dx == -1 ? MV_LEFT  :
13519                         dx == +1 ? MV_RIGHT :
13520                         dy == -1 ? MV_UP    :
13521                         dy == +1 ? MV_DOWN  : MV_NONE);
13522   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13523   int dig_side = MV_DIR_OPPOSITE(move_direction);
13524   int old_element = Feld[jx][jy];
13525   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13526   int collect_count;
13527
13528   if (is_player)                // function can also be called by EL_PENGUIN
13529   {
13530     if (player->MovPos == 0)
13531     {
13532       player->is_digging = FALSE;
13533       player->is_collecting = FALSE;
13534     }
13535
13536     if (player->MovPos == 0)    // last pushing move finished
13537       player->is_pushing = FALSE;
13538
13539     if (mode == DF_NO_PUSH)     // player just stopped pushing
13540     {
13541       player->is_switching = FALSE;
13542       player->push_delay = -1;
13543
13544       return MP_NO_ACTION;
13545     }
13546   }
13547
13548   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13549     old_element = Back[jx][jy];
13550
13551   // in case of element dropped at player position, check background
13552   else if (Back[jx][jy] != EL_EMPTY &&
13553            game.engine_version >= VERSION_IDENT(2,2,0,0))
13554     old_element = Back[jx][jy];
13555
13556   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13557     return MP_NO_ACTION;        // field has no opening in this direction
13558
13559   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13560     return MP_NO_ACTION;        // field has no opening in this direction
13561
13562   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13563   {
13564     SplashAcid(x, y);
13565
13566     Feld[jx][jy] = player->artwork_element;
13567     InitMovingField(jx, jy, MV_DOWN);
13568     Store[jx][jy] = EL_ACID;
13569     ContinueMoving(jx, jy);
13570     BuryPlayer(player);
13571
13572     return MP_DONT_RUN_INTO;
13573   }
13574
13575   if (player_can_move && DONT_RUN_INTO(element))
13576   {
13577     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13578
13579     return MP_DONT_RUN_INTO;
13580   }
13581
13582   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13583     return MP_NO_ACTION;
13584
13585   collect_count = element_info[element].collect_count_initial;
13586
13587   if (!is_player && !IS_COLLECTIBLE(element))   // penguin cannot collect it
13588     return MP_NO_ACTION;
13589
13590   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13591     player_can_move = player_can_move_or_snap;
13592
13593   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13594       game.engine_version >= VERSION_IDENT(2,2,0,0))
13595   {
13596     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13597                                player->index_bit, dig_side);
13598     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13599                                         player->index_bit, dig_side);
13600
13601     if (element == EL_DC_LANDMINE)
13602       Bang(x, y);
13603
13604     if (Feld[x][y] != element)          // field changed by snapping
13605       return MP_ACTION;
13606
13607     return MP_NO_ACTION;
13608   }
13609
13610   if (player->gravity && is_player && !player->is_auto_moving &&
13611       canFallDown(player) && move_direction != MV_DOWN &&
13612       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13613     return MP_NO_ACTION;        // player cannot walk here due to gravity
13614
13615   if (player_can_move &&
13616       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13617   {
13618     int sound_element = SND_ELEMENT(element);
13619     int sound_action = ACTION_WALKING;
13620
13621     if (IS_RND_GATE(element))
13622     {
13623       if (!player->key[RND_GATE_NR(element)])
13624         return MP_NO_ACTION;
13625     }
13626     else if (IS_RND_GATE_GRAY(element))
13627     {
13628       if (!player->key[RND_GATE_GRAY_NR(element)])
13629         return MP_NO_ACTION;
13630     }
13631     else if (IS_RND_GATE_GRAY_ACTIVE(element))
13632     {
13633       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13634         return MP_NO_ACTION;
13635     }
13636     else if (element == EL_EXIT_OPEN ||
13637              element == EL_EM_EXIT_OPEN ||
13638              element == EL_EM_EXIT_OPENING ||
13639              element == EL_STEEL_EXIT_OPEN ||
13640              element == EL_EM_STEEL_EXIT_OPEN ||
13641              element == EL_EM_STEEL_EXIT_OPENING ||
13642              element == EL_SP_EXIT_OPEN ||
13643              element == EL_SP_EXIT_OPENING)
13644     {
13645       sound_action = ACTION_PASSING;    // player is passing exit
13646     }
13647     else if (element == EL_EMPTY)
13648     {
13649       sound_action = ACTION_MOVING;             // nothing to walk on
13650     }
13651
13652     // play sound from background or player, whatever is available
13653     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13654       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13655     else
13656       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13657   }
13658   else if (player_can_move &&
13659            IS_PASSABLE(element) && canPassField(x, y, move_direction))
13660   {
13661     if (!ACCESS_FROM(element, opposite_direction))
13662       return MP_NO_ACTION;      // field not accessible from this direction
13663
13664     if (CAN_MOVE(element))      // only fixed elements can be passed!
13665       return MP_NO_ACTION;
13666
13667     if (IS_EM_GATE(element))
13668     {
13669       if (!player->key[EM_GATE_NR(element)])
13670         return MP_NO_ACTION;
13671     }
13672     else if (IS_EM_GATE_GRAY(element))
13673     {
13674       if (!player->key[EM_GATE_GRAY_NR(element)])
13675         return MP_NO_ACTION;
13676     }
13677     else if (IS_EM_GATE_GRAY_ACTIVE(element))
13678     {
13679       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13680         return MP_NO_ACTION;
13681     }
13682     else if (IS_EMC_GATE(element))
13683     {
13684       if (!player->key[EMC_GATE_NR(element)])
13685         return MP_NO_ACTION;
13686     }
13687     else if (IS_EMC_GATE_GRAY(element))
13688     {
13689       if (!player->key[EMC_GATE_GRAY_NR(element)])
13690         return MP_NO_ACTION;
13691     }
13692     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13693     {
13694       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13695         return MP_NO_ACTION;
13696     }
13697     else if (element == EL_DC_GATE_WHITE ||
13698              element == EL_DC_GATE_WHITE_GRAY ||
13699              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13700     {
13701       if (player->num_white_keys == 0)
13702         return MP_NO_ACTION;
13703
13704       player->num_white_keys--;
13705     }
13706     else if (IS_SP_PORT(element))
13707     {
13708       if (element == EL_SP_GRAVITY_PORT_LEFT ||
13709           element == EL_SP_GRAVITY_PORT_RIGHT ||
13710           element == EL_SP_GRAVITY_PORT_UP ||
13711           element == EL_SP_GRAVITY_PORT_DOWN)
13712         player->gravity = !player->gravity;
13713       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13714                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13715                element == EL_SP_GRAVITY_ON_PORT_UP ||
13716                element == EL_SP_GRAVITY_ON_PORT_DOWN)
13717         player->gravity = TRUE;
13718       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13719                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13720                element == EL_SP_GRAVITY_OFF_PORT_UP ||
13721                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13722         player->gravity = FALSE;
13723     }
13724
13725     // automatically move to the next field with double speed
13726     player->programmed_action = move_direction;
13727
13728     if (player->move_delay_reset_counter == 0)
13729     {
13730       player->move_delay_reset_counter = 2;     // two double speed steps
13731
13732       DOUBLE_PLAYER_SPEED(player);
13733     }
13734
13735     PlayLevelSoundAction(x, y, ACTION_PASSING);
13736   }
13737   else if (player_can_move_or_snap && IS_DIGGABLE(element))
13738   {
13739     RemoveField(x, y);
13740
13741     if (mode != DF_SNAP)
13742     {
13743       GfxElement[x][y] = GFX_ELEMENT(element);
13744       player->is_digging = TRUE;
13745     }
13746
13747     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13748
13749     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13750                                         player->index_bit, dig_side);
13751
13752     if (mode == DF_SNAP)
13753     {
13754       if (level.block_snap_field)
13755         setFieldForSnapping(x, y, element, move_direction);
13756       else
13757         TestIfElementTouchesCustomElement(x, y);        // for empty space
13758
13759       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13760                                           player->index_bit, dig_side);
13761     }
13762   }
13763   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13764   {
13765     RemoveField(x, y);
13766
13767     if (is_player && mode != DF_SNAP)
13768     {
13769       GfxElement[x][y] = element;
13770       player->is_collecting = TRUE;
13771     }
13772
13773     if (element == EL_SPEED_PILL)
13774     {
13775       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13776     }
13777     else if (element == EL_EXTRA_TIME && level.time > 0)
13778     {
13779       TimeLeft += level.extra_time;
13780
13781       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13782
13783       DisplayGameControlValues();
13784     }
13785     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13786     {
13787       player->shield_normal_time_left += level.shield_normal_time;
13788       if (element == EL_SHIELD_DEADLY)
13789         player->shield_deadly_time_left += level.shield_deadly_time;
13790     }
13791     else if (element == EL_DYNAMITE ||
13792              element == EL_EM_DYNAMITE ||
13793              element == EL_SP_DISK_RED)
13794     {
13795       if (player->inventory_size < MAX_INVENTORY_SIZE)
13796         player->inventory_element[player->inventory_size++] = element;
13797
13798       DrawGameDoorValues();
13799     }
13800     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13801     {
13802       player->dynabomb_count++;
13803       player->dynabombs_left++;
13804     }
13805     else if (element == EL_DYNABOMB_INCREASE_SIZE)
13806     {
13807       player->dynabomb_size++;
13808     }
13809     else if (element == EL_DYNABOMB_INCREASE_POWER)
13810     {
13811       player->dynabomb_xl = TRUE;
13812     }
13813     else if (IS_KEY(element))
13814     {
13815       player->key[KEY_NR(element)] = TRUE;
13816
13817       DrawGameDoorValues();
13818     }
13819     else if (element == EL_DC_KEY_WHITE)
13820     {
13821       player->num_white_keys++;
13822
13823       // display white keys?
13824       // DrawGameDoorValues();
13825     }
13826     else if (IS_ENVELOPE(element))
13827     {
13828       player->show_envelope = element;
13829     }
13830     else if (element == EL_EMC_LENSES)
13831     {
13832       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13833
13834       RedrawAllInvisibleElementsForLenses();
13835     }
13836     else if (element == EL_EMC_MAGNIFIER)
13837     {
13838       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13839
13840       RedrawAllInvisibleElementsForMagnifier();
13841     }
13842     else if (IS_DROPPABLE(element) ||
13843              IS_THROWABLE(element))     // can be collected and dropped
13844     {
13845       int i;
13846
13847       if (collect_count == 0)
13848         player->inventory_infinite_element = element;
13849       else
13850         for (i = 0; i < collect_count; i++)
13851           if (player->inventory_size < MAX_INVENTORY_SIZE)
13852             player->inventory_element[player->inventory_size++] = element;
13853
13854       DrawGameDoorValues();
13855     }
13856     else if (collect_count > 0)
13857     {
13858       local_player->gems_still_needed -= collect_count;
13859       if (local_player->gems_still_needed < 0)
13860         local_player->gems_still_needed = 0;
13861
13862       game.snapshot.collected_item = TRUE;
13863
13864       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
13865
13866       DisplayGameControlValues();
13867     }
13868
13869     RaiseScoreElement(element);
13870     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13871
13872     if (is_player)
13873       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13874                                           player->index_bit, dig_side);
13875
13876     if (mode == DF_SNAP)
13877     {
13878       if (level.block_snap_field)
13879         setFieldForSnapping(x, y, element, move_direction);
13880       else
13881         TestIfElementTouchesCustomElement(x, y);        // for empty space
13882
13883       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13884                                           player->index_bit, dig_side);
13885     }
13886   }
13887   else if (player_can_move_or_snap && IS_PUSHABLE(element))
13888   {
13889     if (mode == DF_SNAP && element != EL_BD_ROCK)
13890       return MP_NO_ACTION;
13891
13892     if (CAN_FALL(element) && dy)
13893       return MP_NO_ACTION;
13894
13895     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13896         !(element == EL_SPRING && level.use_spring_bug))
13897       return MP_NO_ACTION;
13898
13899     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13900         ((move_direction & MV_VERTICAL &&
13901           ((element_info[element].move_pattern & MV_LEFT &&
13902             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13903            (element_info[element].move_pattern & MV_RIGHT &&
13904             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13905          (move_direction & MV_HORIZONTAL &&
13906           ((element_info[element].move_pattern & MV_UP &&
13907             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13908            (element_info[element].move_pattern & MV_DOWN &&
13909             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13910       return MP_NO_ACTION;
13911
13912     // do not push elements already moving away faster than player
13913     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13914         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13915       return MP_NO_ACTION;
13916
13917     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13918     {
13919       if (player->push_delay_value == -1 || !player_was_pushing)
13920         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13921     }
13922     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13923     {
13924       if (player->push_delay_value == -1)
13925         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13926     }
13927     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13928     {
13929       if (!player->is_pushing)
13930         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13931     }
13932
13933     player->is_pushing = TRUE;
13934     player->is_active = TRUE;
13935
13936     if (!(IN_LEV_FIELD(nextx, nexty) &&
13937           (IS_FREE(nextx, nexty) ||
13938            (IS_SB_ELEMENT(element) &&
13939             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
13940            (IS_CUSTOM_ELEMENT(element) &&
13941             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
13942       return MP_NO_ACTION;
13943
13944     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13945       return MP_NO_ACTION;
13946
13947     if (player->push_delay == -1)       // new pushing; restart delay
13948       player->push_delay = 0;
13949
13950     if (player->push_delay < player->push_delay_value &&
13951         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13952         element != EL_SPRING && element != EL_BALLOON)
13953     {
13954       // make sure that there is no move delay before next try to push
13955       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13956         player->move_delay = 0;
13957
13958       return MP_NO_ACTION;
13959     }
13960
13961     if (IS_CUSTOM_ELEMENT(element) &&
13962         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
13963     {
13964       if (!DigFieldByCE(nextx, nexty, element))
13965         return MP_NO_ACTION;
13966     }
13967
13968     if (IS_SB_ELEMENT(element))
13969     {
13970       boolean sokoban_task_solved = FALSE;
13971
13972       if (element == EL_SOKOBAN_FIELD_FULL)
13973       {
13974         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13975
13976         IncrementPlayerSokobanFieldsNeeded(local_player);
13977         IncrementPlayerSokobanObjectsNeeded(local_player);
13978       }
13979
13980       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
13981       {
13982         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
13983
13984         DecrementPlayerSokobanFieldsNeeded(local_player);
13985         DecrementPlayerSokobanObjectsNeeded(local_player);
13986
13987         // sokoban object was pushed from empty field to sokoban field
13988         if (Back[x][y] == EL_EMPTY)
13989           sokoban_task_solved = TRUE;
13990       }
13991
13992       Feld[x][y] = EL_SOKOBAN_OBJECT;
13993
13994       if (Back[x][y] == Back[nextx][nexty])
13995         PlayLevelSoundAction(x, y, ACTION_PUSHING);
13996       else if (Back[x][y] != 0)
13997         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
13998                                     ACTION_EMPTYING);
13999       else
14000         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14001                                     ACTION_FILLING);
14002
14003       if (sokoban_task_solved &&
14004           local_player->sokoban_fields_still_needed == 0 &&
14005           local_player->sokoban_objects_still_needed == 0 &&
14006           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
14007       {
14008         local_player->players_still_needed = 0;
14009
14010         PlayerWins(local_player);
14011
14012         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14013       }
14014     }
14015     else
14016       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14017
14018     InitMovingField(x, y, move_direction);
14019     GfxAction[x][y] = ACTION_PUSHING;
14020
14021     if (mode == DF_SNAP)
14022       ContinueMoving(x, y);
14023     else
14024       MovPos[x][y] = (dx != 0 ? dx : dy);
14025
14026     Pushed[x][y] = TRUE;
14027     Pushed[nextx][nexty] = TRUE;
14028
14029     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14030       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14031     else
14032       player->push_delay_value = -1;    // get new value later
14033
14034     // check for element change _after_ element has been pushed
14035     if (game.use_change_when_pushing_bug)
14036     {
14037       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14038                                  player->index_bit, dig_side);
14039       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14040                                           player->index_bit, dig_side);
14041     }
14042   }
14043   else if (IS_SWITCHABLE(element))
14044   {
14045     if (PLAYER_SWITCHING(player, x, y))
14046     {
14047       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14048                                           player->index_bit, dig_side);
14049
14050       return MP_ACTION;
14051     }
14052
14053     player->is_switching = TRUE;
14054     player->switch_x = x;
14055     player->switch_y = y;
14056
14057     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14058
14059     if (element == EL_ROBOT_WHEEL)
14060     {
14061       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14062       ZX = x;
14063       ZY = y;
14064
14065       game.robot_wheel_active = TRUE;
14066
14067       TEST_DrawLevelField(x, y);
14068     }
14069     else if (element == EL_SP_TERMINAL)
14070     {
14071       int xx, yy;
14072
14073       SCAN_PLAYFIELD(xx, yy)
14074       {
14075         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
14076         {
14077           Bang(xx, yy);
14078         }
14079         else if (Feld[xx][yy] == EL_SP_TERMINAL)
14080         {
14081           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14082
14083           ResetGfxAnimation(xx, yy);
14084           TEST_DrawLevelField(xx, yy);
14085         }
14086       }
14087     }
14088     else if (IS_BELT_SWITCH(element))
14089     {
14090       ToggleBeltSwitch(x, y);
14091     }
14092     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14093              element == EL_SWITCHGATE_SWITCH_DOWN ||
14094              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14095              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14096     {
14097       ToggleSwitchgateSwitch(x, y);
14098     }
14099     else if (element == EL_LIGHT_SWITCH ||
14100              element == EL_LIGHT_SWITCH_ACTIVE)
14101     {
14102       ToggleLightSwitch(x, y);
14103     }
14104     else if (element == EL_TIMEGATE_SWITCH ||
14105              element == EL_DC_TIMEGATE_SWITCH)
14106     {
14107       ActivateTimegateSwitch(x, y);
14108     }
14109     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14110              element == EL_BALLOON_SWITCH_RIGHT ||
14111              element == EL_BALLOON_SWITCH_UP    ||
14112              element == EL_BALLOON_SWITCH_DOWN  ||
14113              element == EL_BALLOON_SWITCH_NONE  ||
14114              element == EL_BALLOON_SWITCH_ANY)
14115     {
14116       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14117                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14118                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14119                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14120                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14121                              move_direction);
14122     }
14123     else if (element == EL_LAMP)
14124     {
14125       Feld[x][y] = EL_LAMP_ACTIVE;
14126       local_player->lights_still_needed--;
14127
14128       ResetGfxAnimation(x, y);
14129       TEST_DrawLevelField(x, y);
14130     }
14131     else if (element == EL_TIME_ORB_FULL)
14132     {
14133       Feld[x][y] = EL_TIME_ORB_EMPTY;
14134
14135       if (level.time > 0 || level.use_time_orb_bug)
14136       {
14137         TimeLeft += level.time_orb_time;
14138         game.no_time_limit = FALSE;
14139
14140         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14141
14142         DisplayGameControlValues();
14143       }
14144
14145       ResetGfxAnimation(x, y);
14146       TEST_DrawLevelField(x, y);
14147     }
14148     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14149              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14150     {
14151       int xx, yy;
14152
14153       game.ball_state = !game.ball_state;
14154
14155       SCAN_PLAYFIELD(xx, yy)
14156       {
14157         int e = Feld[xx][yy];
14158
14159         if (game.ball_state)
14160         {
14161           if (e == EL_EMC_MAGIC_BALL)
14162             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14163           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14164             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14165         }
14166         else
14167         {
14168           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14169             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14170           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14171             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14172         }
14173       }
14174     }
14175
14176     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14177                                         player->index_bit, dig_side);
14178
14179     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14180                                         player->index_bit, dig_side);
14181
14182     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14183                                         player->index_bit, dig_side);
14184
14185     return MP_ACTION;
14186   }
14187   else
14188   {
14189     if (!PLAYER_SWITCHING(player, x, y))
14190     {
14191       player->is_switching = TRUE;
14192       player->switch_x = x;
14193       player->switch_y = y;
14194
14195       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14196                                  player->index_bit, dig_side);
14197       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14198                                           player->index_bit, dig_side);
14199
14200       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14201                                  player->index_bit, dig_side);
14202       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14203                                           player->index_bit, dig_side);
14204     }
14205
14206     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14207                                player->index_bit, dig_side);
14208     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14209                                         player->index_bit, dig_side);
14210
14211     return MP_NO_ACTION;
14212   }
14213
14214   player->push_delay = -1;
14215
14216   if (is_player)                // function can also be called by EL_PENGUIN
14217   {
14218     if (Feld[x][y] != element)          // really digged/collected something
14219     {
14220       player->is_collecting = !player->is_digging;
14221       player->is_active = TRUE;
14222     }
14223   }
14224
14225   return MP_MOVING;
14226 }
14227
14228 static boolean DigFieldByCE(int x, int y, int digging_element)
14229 {
14230   int element = Feld[x][y];
14231
14232   if (!IS_FREE(x, y))
14233   {
14234     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14235                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14236                   ACTION_BREAKING);
14237
14238     // no element can dig solid indestructible elements
14239     if (IS_INDESTRUCTIBLE(element) &&
14240         !IS_DIGGABLE(element) &&
14241         !IS_COLLECTIBLE(element))
14242       return FALSE;
14243
14244     if (AmoebaNr[x][y] &&
14245         (element == EL_AMOEBA_FULL ||
14246          element == EL_BD_AMOEBA ||
14247          element == EL_AMOEBA_GROWING))
14248     {
14249       AmoebaCnt[AmoebaNr[x][y]]--;
14250       AmoebaCnt2[AmoebaNr[x][y]]--;
14251     }
14252
14253     if (IS_MOVING(x, y))
14254       RemoveMovingField(x, y);
14255     else
14256     {
14257       RemoveField(x, y);
14258       TEST_DrawLevelField(x, y);
14259     }
14260
14261     // if digged element was about to explode, prevent the explosion
14262     ExplodeField[x][y] = EX_TYPE_NONE;
14263
14264     PlayLevelSoundAction(x, y, action);
14265   }
14266
14267   Store[x][y] = EL_EMPTY;
14268
14269   // this makes it possible to leave the removed element again
14270   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14271     Store[x][y] = element;
14272
14273   return TRUE;
14274 }
14275
14276 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14277 {
14278   int jx = player->jx, jy = player->jy;
14279   int x = jx + dx, y = jy + dy;
14280   int snap_direction = (dx == -1 ? MV_LEFT  :
14281                         dx == +1 ? MV_RIGHT :
14282                         dy == -1 ? MV_UP    :
14283                         dy == +1 ? MV_DOWN  : MV_NONE);
14284   boolean can_continue_snapping = (level.continuous_snapping &&
14285                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14286
14287   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14288     return FALSE;
14289
14290   if (!player->active || !IN_LEV_FIELD(x, y))
14291     return FALSE;
14292
14293   if (dx && dy)
14294     return FALSE;
14295
14296   if (!dx && !dy)
14297   {
14298     if (player->MovPos == 0)
14299       player->is_pushing = FALSE;
14300
14301     player->is_snapping = FALSE;
14302
14303     if (player->MovPos == 0)
14304     {
14305       player->is_moving = FALSE;
14306       player->is_digging = FALSE;
14307       player->is_collecting = FALSE;
14308     }
14309
14310     return FALSE;
14311   }
14312
14313   // prevent snapping with already pressed snap key when not allowed
14314   if (player->is_snapping && !can_continue_snapping)
14315     return FALSE;
14316
14317   player->MovDir = snap_direction;
14318
14319   if (player->MovPos == 0)
14320   {
14321     player->is_moving = FALSE;
14322     player->is_digging = FALSE;
14323     player->is_collecting = FALSE;
14324   }
14325
14326   player->is_dropping = FALSE;
14327   player->is_dropping_pressed = FALSE;
14328   player->drop_pressed_delay = 0;
14329
14330   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14331     return FALSE;
14332
14333   player->is_snapping = TRUE;
14334   player->is_active = TRUE;
14335
14336   if (player->MovPos == 0)
14337   {
14338     player->is_moving = FALSE;
14339     player->is_digging = FALSE;
14340     player->is_collecting = FALSE;
14341   }
14342
14343   if (player->MovPos != 0)      // prevent graphic bugs in versions < 2.2.0
14344     TEST_DrawLevelField(player->last_jx, player->last_jy);
14345
14346   TEST_DrawLevelField(x, y);
14347
14348   return TRUE;
14349 }
14350
14351 static boolean DropElement(struct PlayerInfo *player)
14352 {
14353   int old_element, new_element;
14354   int dropx = player->jx, dropy = player->jy;
14355   int drop_direction = player->MovDir;
14356   int drop_side = drop_direction;
14357   int drop_element = get_next_dropped_element(player);
14358
14359   /* do not drop an element on top of another element; when holding drop key
14360      pressed without moving, dropped element must move away before the next
14361      element can be dropped (this is especially important if the next element
14362      is dynamite, which can be placed on background for historical reasons) */
14363   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14364     return MP_ACTION;
14365
14366   if (IS_THROWABLE(drop_element))
14367   {
14368     dropx += GET_DX_FROM_DIR(drop_direction);
14369     dropy += GET_DY_FROM_DIR(drop_direction);
14370
14371     if (!IN_LEV_FIELD(dropx, dropy))
14372       return FALSE;
14373   }
14374
14375   old_element = Feld[dropx][dropy];     // old element at dropping position
14376   new_element = drop_element;           // default: no change when dropping
14377
14378   // check if player is active, not moving and ready to drop
14379   if (!player->active || player->MovPos || player->drop_delay > 0)
14380     return FALSE;
14381
14382   // check if player has anything that can be dropped
14383   if (new_element == EL_UNDEFINED)
14384     return FALSE;
14385
14386   // only set if player has anything that can be dropped
14387   player->is_dropping_pressed = TRUE;
14388
14389   // check if drop key was pressed long enough for EM style dynamite
14390   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14391     return FALSE;
14392
14393   // check if anything can be dropped at the current position
14394   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14395     return FALSE;
14396
14397   // collected custom elements can only be dropped on empty fields
14398   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14399     return FALSE;
14400
14401   if (old_element != EL_EMPTY)
14402     Back[dropx][dropy] = old_element;   // store old element on this field
14403
14404   ResetGfxAnimation(dropx, dropy);
14405   ResetRandomAnimationValue(dropx, dropy);
14406
14407   if (player->inventory_size > 0 ||
14408       player->inventory_infinite_element != EL_UNDEFINED)
14409   {
14410     if (player->inventory_size > 0)
14411     {
14412       player->inventory_size--;
14413
14414       DrawGameDoorValues();
14415
14416       if (new_element == EL_DYNAMITE)
14417         new_element = EL_DYNAMITE_ACTIVE;
14418       else if (new_element == EL_EM_DYNAMITE)
14419         new_element = EL_EM_DYNAMITE_ACTIVE;
14420       else if (new_element == EL_SP_DISK_RED)
14421         new_element = EL_SP_DISK_RED_ACTIVE;
14422     }
14423
14424     Feld[dropx][dropy] = new_element;
14425
14426     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14427       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14428                           el2img(Feld[dropx][dropy]), 0);
14429
14430     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14431
14432     // needed if previous element just changed to "empty" in the last frame
14433     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14434
14435     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14436                                player->index_bit, drop_side);
14437     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14438                                         CE_PLAYER_DROPS_X,
14439                                         player->index_bit, drop_side);
14440
14441     TestIfElementTouchesCustomElement(dropx, dropy);
14442   }
14443   else          // player is dropping a dyna bomb
14444   {
14445     player->dynabombs_left--;
14446
14447     Feld[dropx][dropy] = new_element;
14448
14449     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14450       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14451                           el2img(Feld[dropx][dropy]), 0);
14452
14453     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14454   }
14455
14456   if (Feld[dropx][dropy] == new_element) // uninitialized unless CE change
14457     InitField_WithBug1(dropx, dropy, FALSE);
14458
14459   new_element = Feld[dropx][dropy];     // element might have changed
14460
14461   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14462       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14463   {
14464     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14465       MovDir[dropx][dropy] = drop_direction;
14466
14467     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14468
14469     // do not cause impact style collision by dropping elements that can fall
14470     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14471   }
14472
14473   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14474   player->is_dropping = TRUE;
14475
14476   player->drop_pressed_delay = 0;
14477   player->is_dropping_pressed = FALSE;
14478
14479   player->drop_x = dropx;
14480   player->drop_y = dropy;
14481
14482   return TRUE;
14483 }
14484
14485 // ----------------------------------------------------------------------------
14486 // game sound playing functions
14487 // ----------------------------------------------------------------------------
14488
14489 static int *loop_sound_frame = NULL;
14490 static int *loop_sound_volume = NULL;
14491
14492 void InitPlayLevelSound(void)
14493 {
14494   int num_sounds = getSoundListSize();
14495
14496   checked_free(loop_sound_frame);
14497   checked_free(loop_sound_volume);
14498
14499   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14500   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14501 }
14502
14503 static void PlayLevelSound(int x, int y, int nr)
14504 {
14505   int sx = SCREENX(x), sy = SCREENY(y);
14506   int volume, stereo_position;
14507   int max_distance = 8;
14508   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14509
14510   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14511       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14512     return;
14513
14514   if (!IN_LEV_FIELD(x, y) ||
14515       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14516       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14517     return;
14518
14519   volume = SOUND_MAX_VOLUME;
14520
14521   if (!IN_SCR_FIELD(sx, sy))
14522   {
14523     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14524     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14525
14526     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14527   }
14528
14529   stereo_position = (SOUND_MAX_LEFT +
14530                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14531                      (SCR_FIELDX + 2 * max_distance));
14532
14533   if (IS_LOOP_SOUND(nr))
14534   {
14535     /* This assures that quieter loop sounds do not overwrite louder ones,
14536        while restarting sound volume comparison with each new game frame. */
14537
14538     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14539       return;
14540
14541     loop_sound_volume[nr] = volume;
14542     loop_sound_frame[nr] = FrameCounter;
14543   }
14544
14545   PlaySoundExt(nr, volume, stereo_position, type);
14546 }
14547
14548 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14549 {
14550   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14551                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14552                  y < LEVELY(BY1) ? LEVELY(BY1) :
14553                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14554                  sound_action);
14555 }
14556
14557 static void PlayLevelSoundAction(int x, int y, int action)
14558 {
14559   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14560 }
14561
14562 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14563 {
14564   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14565
14566   if (sound_effect != SND_UNDEFINED)
14567     PlayLevelSound(x, y, sound_effect);
14568 }
14569
14570 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14571                                               int action)
14572 {
14573   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14574
14575   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14576     PlayLevelSound(x, y, sound_effect);
14577 }
14578
14579 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14580 {
14581   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14582
14583   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14584     PlayLevelSound(x, y, sound_effect);
14585 }
14586
14587 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14588 {
14589   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14590
14591   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14592     StopSound(sound_effect);
14593 }
14594
14595 static int getLevelMusicNr(void)
14596 {
14597   if (levelset.music[level_nr] != MUS_UNDEFINED)
14598     return levelset.music[level_nr];            // from config file
14599   else
14600     return MAP_NOCONF_MUSIC(level_nr);          // from music dir
14601 }
14602
14603 static void FadeLevelSounds(void)
14604 {
14605   FadeSounds();
14606 }
14607
14608 static void FadeLevelMusic(void)
14609 {
14610   int music_nr = getLevelMusicNr();
14611   char *curr_music = getCurrentlyPlayingMusicFilename();
14612   char *next_music = getMusicInfoEntryFilename(music_nr);
14613
14614   if (!strEqual(curr_music, next_music))
14615     FadeMusic();
14616 }
14617
14618 void FadeLevelSoundsAndMusic(void)
14619 {
14620   FadeLevelSounds();
14621   FadeLevelMusic();
14622 }
14623
14624 static void PlayLevelMusic(void)
14625 {
14626   int music_nr = getLevelMusicNr();
14627   char *curr_music = getCurrentlyPlayingMusicFilename();
14628   char *next_music = getMusicInfoEntryFilename(music_nr);
14629
14630   if (!strEqual(curr_music, next_music))
14631     PlayMusicLoop(music_nr);
14632 }
14633
14634 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14635 {
14636   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14637   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14638   int x = xx - 1 - offset;
14639   int y = yy - 1 - offset;
14640
14641   switch (sample)
14642   {
14643     case SAMPLE_blank:
14644       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14645       break;
14646
14647     case SAMPLE_roll:
14648       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14649       break;
14650
14651     case SAMPLE_stone:
14652       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14653       break;
14654
14655     case SAMPLE_nut:
14656       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14657       break;
14658
14659     case SAMPLE_crack:
14660       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14661       break;
14662
14663     case SAMPLE_bug:
14664       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14665       break;
14666
14667     case SAMPLE_tank:
14668       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14669       break;
14670
14671     case SAMPLE_android_clone:
14672       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14673       break;
14674
14675     case SAMPLE_android_move:
14676       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14677       break;
14678
14679     case SAMPLE_spring:
14680       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14681       break;
14682
14683     case SAMPLE_slurp:
14684       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14685       break;
14686
14687     case SAMPLE_eater:
14688       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14689       break;
14690
14691     case SAMPLE_eater_eat:
14692       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14693       break;
14694
14695     case SAMPLE_alien:
14696       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14697       break;
14698
14699     case SAMPLE_collect:
14700       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14701       break;
14702
14703     case SAMPLE_diamond:
14704       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14705       break;
14706
14707     case SAMPLE_squash:
14708       // !!! CHECK THIS !!!
14709 #if 1
14710       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14711 #else
14712       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14713 #endif
14714       break;
14715
14716     case SAMPLE_wonderfall:
14717       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14718       break;
14719
14720     case SAMPLE_drip:
14721       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14722       break;
14723
14724     case SAMPLE_push:
14725       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14726       break;
14727
14728     case SAMPLE_dirt:
14729       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14730       break;
14731
14732     case SAMPLE_acid:
14733       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14734       break;
14735
14736     case SAMPLE_ball:
14737       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14738       break;
14739
14740     case SAMPLE_grow:
14741       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14742       break;
14743
14744     case SAMPLE_wonder:
14745       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14746       break;
14747
14748     case SAMPLE_door:
14749       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14750       break;
14751
14752     case SAMPLE_exit_open:
14753       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14754       break;
14755
14756     case SAMPLE_exit_leave:
14757       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14758       break;
14759
14760     case SAMPLE_dynamite:
14761       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14762       break;
14763
14764     case SAMPLE_tick:
14765       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14766       break;
14767
14768     case SAMPLE_press:
14769       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14770       break;
14771
14772     case SAMPLE_wheel:
14773       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14774       break;
14775
14776     case SAMPLE_boom:
14777       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14778       break;
14779
14780     case SAMPLE_die:
14781       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14782       break;
14783
14784     case SAMPLE_time:
14785       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14786       break;
14787
14788     default:
14789       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14790       break;
14791   }
14792 }
14793
14794 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
14795 {
14796   int element = map_element_SP_to_RND(element_sp);
14797   int action = map_action_SP_to_RND(action_sp);
14798   int offset = (setup.sp_show_border_elements ? 0 : 1);
14799   int x = xx - offset;
14800   int y = yy - offset;
14801
14802   PlayLevelSoundElementAction(x, y, element, action);
14803 }
14804
14805 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
14806 {
14807   int element = map_element_MM_to_RND(element_mm);
14808   int action = map_action_MM_to_RND(action_mm);
14809   int offset = 0;
14810   int x = xx - offset;
14811   int y = yy - offset;
14812
14813   if (!IS_MM_ELEMENT(element))
14814     element = EL_MM_DEFAULT;
14815
14816   PlayLevelSoundElementAction(x, y, element, action);
14817 }
14818
14819 void PlaySound_MM(int sound_mm)
14820 {
14821   int sound = map_sound_MM_to_RND(sound_mm);
14822
14823   if (sound == SND_UNDEFINED)
14824     return;
14825
14826   PlaySound(sound);
14827 }
14828
14829 void PlaySoundLoop_MM(int sound_mm)
14830 {
14831   int sound = map_sound_MM_to_RND(sound_mm);
14832
14833   if (sound == SND_UNDEFINED)
14834     return;
14835
14836   PlaySoundLoop(sound);
14837 }
14838
14839 void StopSound_MM(int sound_mm)
14840 {
14841   int sound = map_sound_MM_to_RND(sound_mm);
14842
14843   if (sound == SND_UNDEFINED)
14844     return;
14845
14846   StopSound(sound);
14847 }
14848
14849 void RaiseScore(int value)
14850 {
14851   local_player->score += value;
14852
14853   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
14854
14855   DisplayGameControlValues();
14856 }
14857
14858 void RaiseScoreElement(int element)
14859 {
14860   switch (element)
14861   {
14862     case EL_EMERALD:
14863     case EL_BD_DIAMOND:
14864     case EL_EMERALD_YELLOW:
14865     case EL_EMERALD_RED:
14866     case EL_EMERALD_PURPLE:
14867     case EL_SP_INFOTRON:
14868       RaiseScore(level.score[SC_EMERALD]);
14869       break;
14870     case EL_DIAMOND:
14871       RaiseScore(level.score[SC_DIAMOND]);
14872       break;
14873     case EL_CRYSTAL:
14874       RaiseScore(level.score[SC_CRYSTAL]);
14875       break;
14876     case EL_PEARL:
14877       RaiseScore(level.score[SC_PEARL]);
14878       break;
14879     case EL_BUG:
14880     case EL_BD_BUTTERFLY:
14881     case EL_SP_ELECTRON:
14882       RaiseScore(level.score[SC_BUG]);
14883       break;
14884     case EL_SPACESHIP:
14885     case EL_BD_FIREFLY:
14886     case EL_SP_SNIKSNAK:
14887       RaiseScore(level.score[SC_SPACESHIP]);
14888       break;
14889     case EL_YAMYAM:
14890     case EL_DARK_YAMYAM:
14891       RaiseScore(level.score[SC_YAMYAM]);
14892       break;
14893     case EL_ROBOT:
14894       RaiseScore(level.score[SC_ROBOT]);
14895       break;
14896     case EL_PACMAN:
14897       RaiseScore(level.score[SC_PACMAN]);
14898       break;
14899     case EL_NUT:
14900       RaiseScore(level.score[SC_NUT]);
14901       break;
14902     case EL_DYNAMITE:
14903     case EL_EM_DYNAMITE:
14904     case EL_SP_DISK_RED:
14905     case EL_DYNABOMB_INCREASE_NUMBER:
14906     case EL_DYNABOMB_INCREASE_SIZE:
14907     case EL_DYNABOMB_INCREASE_POWER:
14908       RaiseScore(level.score[SC_DYNAMITE]);
14909       break;
14910     case EL_SHIELD_NORMAL:
14911     case EL_SHIELD_DEADLY:
14912       RaiseScore(level.score[SC_SHIELD]);
14913       break;
14914     case EL_EXTRA_TIME:
14915       RaiseScore(level.extra_time_score);
14916       break;
14917     case EL_KEY_1:
14918     case EL_KEY_2:
14919     case EL_KEY_3:
14920     case EL_KEY_4:
14921     case EL_EM_KEY_1:
14922     case EL_EM_KEY_2:
14923     case EL_EM_KEY_3:
14924     case EL_EM_KEY_4:
14925     case EL_EMC_KEY_5:
14926     case EL_EMC_KEY_6:
14927     case EL_EMC_KEY_7:
14928     case EL_EMC_KEY_8:
14929     case EL_DC_KEY_WHITE:
14930       RaiseScore(level.score[SC_KEY]);
14931       break;
14932     default:
14933       RaiseScore(element_info[element].collect_score);
14934       break;
14935   }
14936 }
14937
14938 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14939 {
14940   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14941   {
14942     // closing door required in case of envelope style request dialogs
14943     if (!skip_request)
14944       CloseDoor(DOOR_CLOSE_1);
14945
14946     if (network.enabled)
14947       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14948     else
14949     {
14950       if (quick_quit)
14951         FadeSkipNextFadeIn();
14952
14953       SetGameStatus(GAME_MODE_MAIN);
14954
14955       DrawMainMenu();
14956     }
14957   }
14958   else          // continue playing the game
14959   {
14960     if (tape.playing && tape.deactivate_display)
14961       TapeDeactivateDisplayOff(TRUE);
14962
14963     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14964
14965     if (tape.playing && tape.deactivate_display)
14966       TapeDeactivateDisplayOn();
14967   }
14968 }
14969
14970 void RequestQuitGame(boolean ask_if_really_quit)
14971 {
14972   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14973   boolean skip_request = AllPlayersGone || quick_quit;
14974
14975   RequestQuitGameExt(skip_request, quick_quit,
14976                      "Do you really want to quit the game?");
14977 }
14978
14979 void RequestRestartGame(char *message)
14980 {
14981   game.restart_game_message = NULL;
14982
14983   boolean has_started_game = hasStartedNetworkGame();
14984   int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM);
14985
14986   if (Request(message, request_mode | REQ_STAY_CLOSED) && has_started_game)
14987   {
14988     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
14989   }
14990   else
14991   {
14992     SetGameStatus(GAME_MODE_MAIN);
14993
14994     DrawMainMenu();
14995   }
14996 }
14997
14998 void CheckGameOver(void)
14999 {
15000   static boolean last_game_over = FALSE;
15001   static int game_over_delay = 0;
15002   int game_over_delay_value = 50;
15003   boolean game_over = checkGameFailed();
15004
15005   // do not handle game over if request dialog is already active
15006   if (game.request_active)
15007     return;
15008
15009   if (!game_over)
15010   {
15011     last_game_over = FALSE;
15012     game_over_delay = game_over_delay_value;
15013
15014     return;
15015   }
15016
15017   if (game_over_delay > 0)
15018   {
15019     game_over_delay--;
15020
15021     return;
15022   }
15023
15024   if (last_game_over != game_over)
15025     game.restart_game_message = (hasStartedNetworkGame() ?
15026                                  "Game over! Play it again?" :
15027                                  "Game over!");
15028
15029   last_game_over = game_over;
15030 }
15031
15032 boolean checkGameSolved(void)
15033 {
15034   // set for all game engines if level was solved
15035   return local_player->LevelSolved_GameEnd;
15036 }
15037
15038 boolean checkGameFailed(void)
15039 {
15040   if (!AllPlayersGone)
15041     return FALSE;
15042
15043   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15044     return (level.native_em_level->lev->home > 0);
15045   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15046     return (game_sp.GameOver && !game_sp.LevelSolved);
15047   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15048     return (game_mm.game_over && !game_mm.level_solved);
15049   else                          // GAME_ENGINE_TYPE_RND
15050     return (local_player->GameOver && !local_player->LevelSolved);
15051 }
15052
15053 boolean checkGameEnded(void)
15054 {
15055   return (checkGameSolved() || checkGameFailed());
15056 }
15057
15058
15059 // ----------------------------------------------------------------------------
15060 // random generator functions
15061 // ----------------------------------------------------------------------------
15062
15063 unsigned int InitEngineRandom_RND(int seed)
15064 {
15065   game.num_random_calls = 0;
15066
15067   return InitEngineRandom(seed);
15068 }
15069
15070 unsigned int RND(int max)
15071 {
15072   if (max > 0)
15073   {
15074     game.num_random_calls++;
15075
15076     return GetEngineRandom(max);
15077   }
15078
15079   return 0;
15080 }
15081
15082
15083 // ----------------------------------------------------------------------------
15084 // game engine snapshot handling functions
15085 // ----------------------------------------------------------------------------
15086
15087 struct EngineSnapshotInfo
15088 {
15089   // runtime values for custom element collect score
15090   int collect_score[NUM_CUSTOM_ELEMENTS];
15091
15092   // runtime values for group element choice position
15093   int choice_pos[NUM_GROUP_ELEMENTS];
15094
15095   // runtime values for belt position animations
15096   int belt_graphic[4][NUM_BELT_PARTS];
15097   int belt_anim_mode[4][NUM_BELT_PARTS];
15098 };
15099
15100 static struct EngineSnapshotInfo engine_snapshot_rnd;
15101 static char *snapshot_level_identifier = NULL;
15102 static int snapshot_level_nr = -1;
15103
15104 static void SaveEngineSnapshotValues_RND(void)
15105 {
15106   static int belt_base_active_element[4] =
15107   {
15108     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15109     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15110     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15111     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15112   };
15113   int i, j;
15114
15115   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15116   {
15117     int element = EL_CUSTOM_START + i;
15118
15119     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15120   }
15121
15122   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15123   {
15124     int element = EL_GROUP_START + i;
15125
15126     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15127   }
15128
15129   for (i = 0; i < 4; i++)
15130   {
15131     for (j = 0; j < NUM_BELT_PARTS; j++)
15132     {
15133       int element = belt_base_active_element[i] + j;
15134       int graphic = el2img(element);
15135       int anim_mode = graphic_info[graphic].anim_mode;
15136
15137       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15138       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15139     }
15140   }
15141 }
15142
15143 static void LoadEngineSnapshotValues_RND(void)
15144 {
15145   unsigned int num_random_calls = game.num_random_calls;
15146   int i, j;
15147
15148   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15149   {
15150     int element = EL_CUSTOM_START + i;
15151
15152     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15153   }
15154
15155   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15156   {
15157     int element = EL_GROUP_START + i;
15158
15159     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15160   }
15161
15162   for (i = 0; i < 4; i++)
15163   {
15164     for (j = 0; j < NUM_BELT_PARTS; j++)
15165     {
15166       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15167       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15168
15169       graphic_info[graphic].anim_mode = anim_mode;
15170     }
15171   }
15172
15173   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15174   {
15175     InitRND(tape.random_seed);
15176     for (i = 0; i < num_random_calls; i++)
15177       RND(1);
15178   }
15179
15180   if (game.num_random_calls != num_random_calls)
15181   {
15182     Error(ERR_INFO, "number of random calls out of sync");
15183     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
15184     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
15185     Error(ERR_EXIT, "this should not happen -- please debug");
15186   }
15187 }
15188
15189 void FreeEngineSnapshotSingle(void)
15190 {
15191   FreeSnapshotSingle();
15192
15193   setString(&snapshot_level_identifier, NULL);
15194   snapshot_level_nr = -1;
15195 }
15196
15197 void FreeEngineSnapshotList(void)
15198 {
15199   FreeSnapshotList();
15200 }
15201
15202 static ListNode *SaveEngineSnapshotBuffers(void)
15203 {
15204   ListNode *buffers = NULL;
15205
15206   // copy some special values to a structure better suited for the snapshot
15207
15208   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15209     SaveEngineSnapshotValues_RND();
15210   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15211     SaveEngineSnapshotValues_EM();
15212   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15213     SaveEngineSnapshotValues_SP(&buffers);
15214   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15215     SaveEngineSnapshotValues_MM(&buffers);
15216
15217   // save values stored in special snapshot structure
15218
15219   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15220     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15221   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15222     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15223   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15224     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15225   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15226     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15227
15228   // save further RND engine values
15229
15230   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15231   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15232   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15233
15234   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZX));
15235   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZY));
15236   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitX));
15237   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitY));
15238
15239   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15240   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15241   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15242   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15243   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15244
15245   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15246   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15247   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15248
15249   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15250
15251   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
15252
15253   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15254   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15255
15256   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Feld));
15257   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15258   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15259   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15260   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15261   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15262   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15263   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15264   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15265   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15266   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15267   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15268   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15269   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15270   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15271   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15272   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15273   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15274
15275   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15276   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15277
15278   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15279   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15280   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15281
15282   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15283   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15284
15285   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15286   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15287   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15288   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15289   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15290
15291   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15292   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15293
15294 #if 0
15295   ListNode *node = engine_snapshot_list_rnd;
15296   int num_bytes = 0;
15297
15298   while (node != NULL)
15299   {
15300     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15301
15302     node = node->next;
15303   }
15304
15305   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
15306 #endif
15307
15308   return buffers;
15309 }
15310
15311 void SaveEngineSnapshotSingle(void)
15312 {
15313   ListNode *buffers = SaveEngineSnapshotBuffers();
15314
15315   // finally save all snapshot buffers to single snapshot
15316   SaveSnapshotSingle(buffers);
15317
15318   // save level identification information
15319   setString(&snapshot_level_identifier, leveldir_current->identifier);
15320   snapshot_level_nr = level_nr;
15321 }
15322
15323 boolean CheckSaveEngineSnapshotToList(void)
15324 {
15325   boolean save_snapshot =
15326     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
15327      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
15328       game.snapshot.changed_action) ||
15329      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15330       game.snapshot.collected_item));
15331
15332   game.snapshot.changed_action = FALSE;
15333   game.snapshot.collected_item = FALSE;
15334   game.snapshot.save_snapshot = save_snapshot;
15335
15336   return save_snapshot;
15337 }
15338
15339 void SaveEngineSnapshotToList(void)
15340 {
15341   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
15342       tape.quick_resume)
15343     return;
15344
15345   ListNode *buffers = SaveEngineSnapshotBuffers();
15346
15347   // finally save all snapshot buffers to snapshot list
15348   SaveSnapshotToList(buffers);
15349 }
15350
15351 void SaveEngineSnapshotToListInitial(void)
15352 {
15353   FreeEngineSnapshotList();
15354
15355   SaveEngineSnapshotToList();
15356 }
15357
15358 static void LoadEngineSnapshotValues(void)
15359 {
15360   // restore special values from snapshot structure
15361
15362   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15363     LoadEngineSnapshotValues_RND();
15364   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15365     LoadEngineSnapshotValues_EM();
15366   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15367     LoadEngineSnapshotValues_SP();
15368   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15369     LoadEngineSnapshotValues_MM();
15370 }
15371
15372 void LoadEngineSnapshotSingle(void)
15373 {
15374   LoadSnapshotSingle();
15375
15376   LoadEngineSnapshotValues();
15377 }
15378
15379 static void LoadEngineSnapshot_Undo(int steps)
15380 {
15381   LoadSnapshotFromList_Older(steps);
15382
15383   LoadEngineSnapshotValues();
15384 }
15385
15386 static void LoadEngineSnapshot_Redo(int steps)
15387 {
15388   LoadSnapshotFromList_Newer(steps);
15389
15390   LoadEngineSnapshotValues();
15391 }
15392
15393 boolean CheckEngineSnapshotSingle(void)
15394 {
15395   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15396           snapshot_level_nr == level_nr);
15397 }
15398
15399 boolean CheckEngineSnapshotList(void)
15400 {
15401   return CheckSnapshotList();
15402 }
15403
15404
15405 // ---------- new game button stuff -------------------------------------------
15406
15407 static struct
15408 {
15409   int graphic;
15410   struct XY *pos;
15411   int gadget_id;
15412   boolean *setup_value;
15413   boolean allowed_on_tape;
15414   char *infotext;
15415 } gamebutton_info[NUM_GAME_BUTTONS] =
15416 {
15417   {
15418     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
15419     GAME_CTRL_ID_STOP,                          NULL,
15420     TRUE,                                       "stop game"
15421   },
15422   {
15423     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
15424     GAME_CTRL_ID_PAUSE,                         NULL,
15425     TRUE,                                       "pause game"
15426   },
15427   {
15428     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
15429     GAME_CTRL_ID_PLAY,                          NULL,
15430     TRUE,                                       "play game"
15431   },
15432   {
15433     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
15434     GAME_CTRL_ID_UNDO,                          NULL,
15435     TRUE,                                       "undo step"
15436   },
15437   {
15438     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
15439     GAME_CTRL_ID_REDO,                          NULL,
15440     TRUE,                                       "redo step"
15441   },
15442   {
15443     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
15444     GAME_CTRL_ID_SAVE,                          NULL,
15445     TRUE,                                       "save game"
15446   },
15447   {
15448     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
15449     GAME_CTRL_ID_PAUSE2,                        NULL,
15450     TRUE,                                       "pause game"
15451   },
15452   {
15453     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
15454     GAME_CTRL_ID_LOAD,                          NULL,
15455     TRUE,                                       "load game"
15456   },
15457   {
15458     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
15459     GAME_CTRL_ID_PANEL_STOP,                    NULL,
15460     FALSE,                                      "stop game"
15461   },
15462   {
15463     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
15464     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
15465     FALSE,                                      "pause game"
15466   },
15467   {
15468     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
15469     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
15470     FALSE,                                      "play game"
15471   },
15472   {
15473     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
15474     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
15475     TRUE,                                       "background music on/off"
15476   },
15477   {
15478     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
15479     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
15480     TRUE,                                       "sound loops on/off"
15481   },
15482   {
15483     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
15484     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
15485     TRUE,                                       "normal sounds on/off"
15486   },
15487   {
15488     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
15489     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
15490     FALSE,                                      "background music on/off"
15491   },
15492   {
15493     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
15494     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
15495     FALSE,                                      "sound loops on/off"
15496   },
15497   {
15498     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
15499     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
15500     FALSE,                                      "normal sounds on/off"
15501   }
15502 };
15503
15504 void CreateGameButtons(void)
15505 {
15506   int i;
15507
15508   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15509   {
15510     int graphic = gamebutton_info[i].graphic;
15511     struct GraphicInfo *gfx = &graphic_info[graphic];
15512     struct XY *pos = gamebutton_info[i].pos;
15513     struct GadgetInfo *gi;
15514     int button_type;
15515     boolean checked;
15516     unsigned int event_mask;
15517     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
15518     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
15519     int base_x = (on_tape ? VX : DX);
15520     int base_y = (on_tape ? VY : DY);
15521     int gd_x   = gfx->src_x;
15522     int gd_y   = gfx->src_y;
15523     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
15524     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
15525     int gd_xa  = gfx->src_x + gfx->active_xoffset;
15526     int gd_ya  = gfx->src_y + gfx->active_yoffset;
15527     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
15528     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
15529     int id = i;
15530
15531     if (gfx->bitmap == NULL)
15532     {
15533       game_gadget[id] = NULL;
15534
15535       continue;
15536     }
15537
15538     if (id == GAME_CTRL_ID_STOP ||
15539         id == GAME_CTRL_ID_PANEL_STOP ||
15540         id == GAME_CTRL_ID_PLAY ||
15541         id == GAME_CTRL_ID_PANEL_PLAY ||
15542         id == GAME_CTRL_ID_SAVE ||
15543         id == GAME_CTRL_ID_LOAD)
15544     {
15545       button_type = GD_TYPE_NORMAL_BUTTON;
15546       checked = FALSE;
15547       event_mask = GD_EVENT_RELEASED;
15548     }
15549     else if (id == GAME_CTRL_ID_UNDO ||
15550              id == GAME_CTRL_ID_REDO)
15551     {
15552       button_type = GD_TYPE_NORMAL_BUTTON;
15553       checked = FALSE;
15554       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
15555     }
15556     else
15557     {
15558       button_type = GD_TYPE_CHECK_BUTTON;
15559       checked = (gamebutton_info[i].setup_value != NULL ?
15560                  *gamebutton_info[i].setup_value : FALSE);
15561       event_mask = GD_EVENT_PRESSED;
15562     }
15563
15564     gi = CreateGadget(GDI_CUSTOM_ID, id,
15565                       GDI_IMAGE_ID, graphic,
15566                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
15567                       GDI_X, base_x + GDI_ACTIVE_POS(pos->x),
15568                       GDI_Y, base_y + GDI_ACTIVE_POS(pos->y),
15569                       GDI_WIDTH, gfx->width,
15570                       GDI_HEIGHT, gfx->height,
15571                       GDI_TYPE, button_type,
15572                       GDI_STATE, GD_BUTTON_UNPRESSED,
15573                       GDI_CHECKED, checked,
15574                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
15575                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
15576                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
15577                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
15578                       GDI_DIRECT_DRAW, FALSE,
15579                       GDI_EVENT_MASK, event_mask,
15580                       GDI_CALLBACK_ACTION, HandleGameButtons,
15581                       GDI_END);
15582
15583     if (gi == NULL)
15584       Error(ERR_EXIT, "cannot create gadget");
15585
15586     game_gadget[id] = gi;
15587   }
15588 }
15589
15590 void FreeGameButtons(void)
15591 {
15592   int i;
15593
15594   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15595     FreeGadget(game_gadget[i]);
15596 }
15597
15598 static void UnmapGameButtonsAtSamePosition(int id)
15599 {
15600   int i;
15601
15602   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15603     if (i != id &&
15604         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15605         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15606       UnmapGadget(game_gadget[i]);
15607 }
15608
15609 static void UnmapGameButtonsAtSamePosition_All(void)
15610 {
15611   if (setup.show_snapshot_buttons)
15612   {
15613     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
15614     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
15615     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
15616   }
15617   else
15618   {
15619     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
15620     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
15621     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
15622
15623     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
15624     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
15625     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
15626   }
15627 }
15628
15629 static void MapGameButtonsAtSamePosition(int id)
15630 {
15631   int i;
15632
15633   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15634     if (i != id &&
15635         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15636         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15637       MapGadget(game_gadget[i]);
15638
15639   UnmapGameButtonsAtSamePosition_All();
15640 }
15641
15642 void MapUndoRedoButtons(void)
15643 {
15644   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15645   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15646
15647   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15648   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15649
15650   ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, TRUE, GDI_END);
15651 }
15652
15653 void UnmapUndoRedoButtons(void)
15654 {
15655   UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15656   UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15657
15658   MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15659   MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15660
15661   ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, FALSE, GDI_END);
15662 }
15663
15664 static void MapGameButtonsExt(boolean on_tape)
15665 {
15666   int i;
15667
15668   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15669     if ((!on_tape || gamebutton_info[i].allowed_on_tape) &&
15670         i != GAME_CTRL_ID_UNDO &&
15671         i != GAME_CTRL_ID_REDO)
15672       MapGadget(game_gadget[i]);
15673
15674   UnmapGameButtonsAtSamePosition_All();
15675
15676   RedrawGameButtons();
15677 }
15678
15679 static void UnmapGameButtonsExt(boolean on_tape)
15680 {
15681   int i;
15682
15683   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15684     if (!on_tape || gamebutton_info[i].allowed_on_tape)
15685       UnmapGadget(game_gadget[i]);
15686 }
15687
15688 static void RedrawGameButtonsExt(boolean on_tape)
15689 {
15690   int i;
15691
15692   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15693     if (!on_tape || gamebutton_info[i].allowed_on_tape)
15694       RedrawGadget(game_gadget[i]);
15695
15696   // RedrawGadget() may have set REDRAW_ALL if buttons are defined off-area
15697   redraw_mask &= ~REDRAW_ALL;
15698 }
15699
15700 static void SetGadgetState(struct GadgetInfo *gi, boolean state)
15701 {
15702   if (gi == NULL)
15703     return;
15704
15705   gi->checked = state;
15706 }
15707
15708 static void RedrawSoundButtonGadget(int id)
15709 {
15710   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
15711              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
15712              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
15713              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
15714              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
15715              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
15716              id);
15717
15718   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
15719   RedrawGadget(game_gadget[id2]);
15720 }
15721
15722 void MapGameButtons(void)
15723 {
15724   MapGameButtonsExt(FALSE);
15725 }
15726
15727 void UnmapGameButtons(void)
15728 {
15729   UnmapGameButtonsExt(FALSE);
15730 }
15731
15732 void RedrawGameButtons(void)
15733 {
15734   RedrawGameButtonsExt(FALSE);
15735 }
15736
15737 void MapGameButtonsOnTape(void)
15738 {
15739   MapGameButtonsExt(TRUE);
15740 }
15741
15742 void UnmapGameButtonsOnTape(void)
15743 {
15744   UnmapGameButtonsExt(TRUE);
15745 }
15746
15747 void RedrawGameButtonsOnTape(void)
15748 {
15749   RedrawGameButtonsExt(TRUE);
15750 }
15751
15752 static void GameUndoRedoExt(void)
15753 {
15754   ClearPlayerAction();
15755
15756   tape.pausing = TRUE;
15757
15758   RedrawPlayfield();
15759   UpdateAndDisplayGameControlValues();
15760
15761   DrawCompleteVideoDisplay();
15762   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
15763   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
15764   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
15765
15766   BackToFront();
15767 }
15768
15769 static void GameUndo(int steps)
15770 {
15771   if (!CheckEngineSnapshotList())
15772     return;
15773
15774   LoadEngineSnapshot_Undo(steps);
15775
15776   GameUndoRedoExt();
15777 }
15778
15779 static void GameRedo(int steps)
15780 {
15781   if (!CheckEngineSnapshotList())
15782     return;
15783
15784   LoadEngineSnapshot_Redo(steps);
15785
15786   GameUndoRedoExt();
15787 }
15788
15789 static void HandleGameButtonsExt(int id, int button)
15790 {
15791   static boolean game_undo_executed = FALSE;
15792   int steps = BUTTON_STEPSIZE(button);
15793   boolean handle_game_buttons =
15794     (game_status == GAME_MODE_PLAYING ||
15795      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
15796
15797   if (!handle_game_buttons)
15798     return;
15799
15800   switch (id)
15801   {
15802     case GAME_CTRL_ID_STOP:
15803     case GAME_CTRL_ID_PANEL_STOP:
15804       if (game_status == GAME_MODE_MAIN)
15805         break;
15806
15807       if (tape.playing)
15808         TapeStop();
15809       else
15810         RequestQuitGame(TRUE);
15811
15812       break;
15813
15814     case GAME_CTRL_ID_PAUSE:
15815     case GAME_CTRL_ID_PAUSE2:
15816     case GAME_CTRL_ID_PANEL_PAUSE:
15817       if (network.enabled && game_status == GAME_MODE_PLAYING)
15818       {
15819         if (tape.pausing)
15820           SendToServer_ContinuePlaying();
15821         else
15822           SendToServer_PausePlaying();
15823       }
15824       else
15825         TapeTogglePause(TAPE_TOGGLE_MANUAL);
15826
15827       game_undo_executed = FALSE;
15828
15829       break;
15830
15831     case GAME_CTRL_ID_PLAY:
15832     case GAME_CTRL_ID_PANEL_PLAY:
15833       if (game_status == GAME_MODE_MAIN)
15834       {
15835         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15836       }
15837       else if (tape.pausing)
15838       {
15839         if (network.enabled)
15840           SendToServer_ContinuePlaying();
15841         else
15842           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
15843       }
15844       break;
15845
15846     case GAME_CTRL_ID_UNDO:
15847       // Important: When using "save snapshot when collecting an item" mode,
15848       // load last (current) snapshot for first "undo" after pressing "pause"
15849       // (else the last-but-one snapshot would be loaded, because the snapshot
15850       // pointer already points to the last snapshot when pressing "pause",
15851       // which is fine for "every step/move" mode, but not for "every collect")
15852       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15853           !game_undo_executed)
15854         steps--;
15855
15856       game_undo_executed = TRUE;
15857
15858       GameUndo(steps);
15859       break;
15860
15861     case GAME_CTRL_ID_REDO:
15862       GameRedo(steps);
15863       break;
15864
15865     case GAME_CTRL_ID_SAVE:
15866       TapeQuickSave();
15867       break;
15868
15869     case GAME_CTRL_ID_LOAD:
15870       TapeQuickLoad();
15871       break;
15872
15873     case SOUND_CTRL_ID_MUSIC:
15874     case SOUND_CTRL_ID_PANEL_MUSIC:
15875       if (setup.sound_music)
15876       { 
15877         setup.sound_music = FALSE;
15878
15879         FadeMusic();
15880       }
15881       else if (audio.music_available)
15882       { 
15883         setup.sound = setup.sound_music = TRUE;
15884
15885         SetAudioMode(setup.sound);
15886
15887         if (game_status == GAME_MODE_PLAYING)
15888           PlayLevelMusic();
15889       }
15890
15891       RedrawSoundButtonGadget(id);
15892
15893       break;
15894
15895     case SOUND_CTRL_ID_LOOPS:
15896     case SOUND_CTRL_ID_PANEL_LOOPS:
15897       if (setup.sound_loops)
15898         setup.sound_loops = FALSE;
15899       else if (audio.loops_available)
15900       {
15901         setup.sound = setup.sound_loops = TRUE;
15902
15903         SetAudioMode(setup.sound);
15904       }
15905
15906       RedrawSoundButtonGadget(id);
15907
15908       break;
15909
15910     case SOUND_CTRL_ID_SIMPLE:
15911     case SOUND_CTRL_ID_PANEL_SIMPLE:
15912       if (setup.sound_simple)
15913         setup.sound_simple = FALSE;
15914       else if (audio.sound_available)
15915       {
15916         setup.sound = setup.sound_simple = TRUE;
15917
15918         SetAudioMode(setup.sound);
15919       }
15920
15921       RedrawSoundButtonGadget(id);
15922
15923       break;
15924
15925     default:
15926       break;
15927   }
15928 }
15929
15930 static void HandleGameButtons(struct GadgetInfo *gi)
15931 {
15932   HandleGameButtonsExt(gi->custom_id, gi->event.button);
15933 }
15934
15935 void HandleSoundButtonKeys(Key key)
15936 {
15937   if (key == setup.shortcut.sound_simple)
15938     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
15939   else if (key == setup.shortcut.sound_loops)
15940     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
15941   else if (key == setup.shortcut.sound_music)
15942     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
15943 }