added variables to prevent repeating expression
[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 //                  https://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_STEP_DELAY(e)   (   (element_info[e].step_delay_fixed) + \
883                                  RND(element_info[e].step_delay_random))
884 #define GET_MAX_STEP_DELAY(e)   (   (element_info[e].step_delay_fixed) + \
885                                     (element_info[e].step_delay_random))
886 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
887                                  RND(element_info[e].ce_value_random_initial))
888 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
889 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
890                                  RND((c)->delay_random * (c)->delay_frames))
891 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
892                                  RND((c)->delay_random))
893
894
895 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
896          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
897
898 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
899         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
900          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
901          (be) + (e) - EL_SELF)
902
903 #define GET_PLAYER_FROM_BITS(p)                                         \
904         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
905
906 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
907         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
908          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
909          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
910          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
911          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
912          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
913          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
914          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
915          (e))
916
917 #define CAN_GROW_INTO(e)                                                \
918         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
919
920 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
921                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
922                                         (condition)))
923
924 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
925                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
926                                         (CAN_MOVE_INTO_ACID(e) &&       \
927                                          Tile[x][y] == EL_ACID) ||      \
928                                         (condition)))
929
930 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
931                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
932                                         (CAN_MOVE_INTO_ACID(e) &&       \
933                                          Tile[x][y] == EL_ACID) ||      \
934                                         (condition)))
935
936 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
937                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
938                                         (condition) ||                  \
939                                         (CAN_MOVE_INTO_ACID(e) &&       \
940                                          Tile[x][y] == EL_ACID) ||      \
941                                         (DONT_COLLIDE_WITH(e) &&        \
942                                          IS_PLAYER(x, y) &&             \
943                                          !PLAYER_ENEMY_PROTECTED(x, y))))
944
945 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
946         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
947
948 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
949         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
950
951 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
952         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Tile[x][y] == EL_EMC_PLANT)
953
954 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
955         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Tile[x][y]) || \
956                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
957
958 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
959         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
960
961 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
962         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Tile[x][y] == EL_DIAMOND)
963
964 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
965         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_FOOD_DARK_YAMYAM(Tile[x][y]))
966
967 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
968         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Tile[x][y]))
969
970 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
971         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Tile[x][y]))
972
973 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
974         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Tile[x][y] == EL_EXIT_OPEN || \
975                                                  Tile[x][y] == EL_EM_EXIT_OPEN || \
976                                                  Tile[x][y] == EL_STEEL_EXIT_OPEN || \
977                                                  Tile[x][y] == EL_EM_STEEL_EXIT_OPEN || \
978                                                  IS_FOOD_PENGUIN(Tile[x][y])))
979 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
980         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
981
982 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
983         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
984
985 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
986         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
987
988 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
989         (IN_LEV_FIELD(x, y) && (Tile[x][y] == EL_EMC_SPRING_BUMPER ||   \
990                                 Tile[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
991
992 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
993
994 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
995                 (!IS_PLAYER(x, y) &&                                    \
996                  IS_EQUAL_OR_IN_GROUP(Tile[x][y], MOVE_ENTER_EL(e)))
997
998 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
999         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
1000
1001 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
1002 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
1003
1004 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
1005 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
1006 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
1007 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1008
1009 #define MM_HEALTH(x)            (MIN(MAX(0, MAX_HEALTH - (x)), MAX_HEALTH))
1010
1011 // game button identifiers
1012 #define GAME_CTRL_ID_STOP               0
1013 #define GAME_CTRL_ID_PAUSE              1
1014 #define GAME_CTRL_ID_PLAY               2
1015 #define GAME_CTRL_ID_UNDO               3
1016 #define GAME_CTRL_ID_REDO               4
1017 #define GAME_CTRL_ID_SAVE               5
1018 #define GAME_CTRL_ID_PAUSE2             6
1019 #define GAME_CTRL_ID_LOAD               7
1020 #define GAME_CTRL_ID_PANEL_STOP         8
1021 #define GAME_CTRL_ID_PANEL_PAUSE        9
1022 #define GAME_CTRL_ID_PANEL_PLAY         10
1023 #define GAME_CTRL_ID_TOUCH_STOP         11
1024 #define GAME_CTRL_ID_TOUCH_PAUSE        12
1025 #define SOUND_CTRL_ID_MUSIC             13
1026 #define SOUND_CTRL_ID_LOOPS             14
1027 #define SOUND_CTRL_ID_SIMPLE            15
1028 #define SOUND_CTRL_ID_PANEL_MUSIC       16
1029 #define SOUND_CTRL_ID_PANEL_LOOPS       17
1030 #define SOUND_CTRL_ID_PANEL_SIMPLE      18
1031
1032 #define NUM_GAME_BUTTONS                19
1033
1034
1035 // forward declaration for internal use
1036
1037 static void CreateField(int, int, int);
1038
1039 static void ResetGfxAnimation(int, int);
1040
1041 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1042 static void AdvanceFrameAndPlayerCounters(int);
1043
1044 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1045 static boolean MovePlayer(struct PlayerInfo *, int, int);
1046 static void ScrollPlayer(struct PlayerInfo *, int);
1047 static void ScrollScreen(struct PlayerInfo *, int);
1048
1049 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1050 static boolean DigFieldByCE(int, int, int);
1051 static boolean SnapField(struct PlayerInfo *, int, int);
1052 static boolean DropElement(struct PlayerInfo *);
1053
1054 static void InitBeltMovement(void);
1055 static void CloseAllOpenTimegates(void);
1056 static void CheckGravityMovement(struct PlayerInfo *);
1057 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1058 static void KillPlayerUnlessEnemyProtected(int, int);
1059 static void KillPlayerUnlessExplosionProtected(int, int);
1060
1061 static void CheckNextToConditions(int, int);
1062 static void TestIfPlayerNextToCustomElement(int, int);
1063 static void TestIfPlayerTouchesCustomElement(int, int);
1064 static void TestIfElementNextToCustomElement(int, int);
1065 static void TestIfElementTouchesCustomElement(int, int);
1066 static void TestIfElementHitsCustomElement(int, int, int);
1067
1068 static void HandleElementChange(int, int, int);
1069 static void ExecuteCustomElementAction(int, int, int, int);
1070 static boolean ChangeElement(int, int, int, int);
1071
1072 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int, int, int);
1073 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1074         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, -1)
1075 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1076         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1077 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1078         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1079 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1080         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1081 #define CheckTriggeredElementChangeByMouse(x, y, e, ev, s)              \
1082         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1083
1084 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1085 #define CheckElementChange(x, y, e, te, ev)                             \
1086         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1087 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1088         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1089 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1090         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1091 #define CheckElementChangeByMouse(x, y, e, ev, s)                       \
1092         CheckElementChangeExt(x, y, e, EL_UNDEFINED, ev, CH_PLAYER_ANY, s)
1093
1094 static void PlayLevelSound(int, int, int);
1095 static void PlayLevelSoundNearest(int, int, int);
1096 static void PlayLevelSoundAction(int, int, int);
1097 static void PlayLevelSoundElementAction(int, int, int, int);
1098 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1099 static void PlayLevelSoundActionIfLoop(int, int, int);
1100 static void StopLevelSoundActionIfLoop(int, int, int);
1101 static void PlayLevelMusic(void);
1102 static void FadeLevelSoundsAndMusic(void);
1103
1104 static void HandleGameButtons(struct GadgetInfo *);
1105
1106 int AmoebaNeighbourNr(int, int);
1107 void AmoebaToDiamond(int, int);
1108 void ContinueMoving(int, int);
1109 void Bang(int, int);
1110 void InitMovDir(int, int);
1111 void InitAmoebaNr(int, int);
1112 void NewHighScore(int, boolean);
1113
1114 void TestIfGoodThingHitsBadThing(int, int, int);
1115 void TestIfBadThingHitsGoodThing(int, int, int);
1116 void TestIfPlayerTouchesBadThing(int, int);
1117 void TestIfPlayerRunsIntoBadThing(int, int, int);
1118 void TestIfBadThingTouchesPlayer(int, int);
1119 void TestIfBadThingRunsIntoPlayer(int, int, int);
1120 void TestIfFriendTouchesBadThing(int, int);
1121 void TestIfBadThingTouchesFriend(int, int);
1122 void TestIfBadThingTouchesOtherBadThing(int, int);
1123 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1124
1125 void KillPlayer(struct PlayerInfo *);
1126 void BuryPlayer(struct PlayerInfo *);
1127 void RemovePlayer(struct PlayerInfo *);
1128 void ExitPlayer(struct PlayerInfo *);
1129
1130 static int getInvisibleActiveFromInvisibleElement(int);
1131 static int getInvisibleFromInvisibleActiveElement(int);
1132
1133 static void TestFieldAfterSnapping(int, int, int, int, int);
1134
1135 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1136
1137 // for detection of endless loops, caused by custom element programming
1138 // (using maximal playfield width x 10 is just a rough approximation)
1139 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1140
1141 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1142 {                                                                       \
1143   if (recursion_loop_detected)                                          \
1144     return (rc);                                                        \
1145                                                                         \
1146   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1147   {                                                                     \
1148     recursion_loop_detected = TRUE;                                     \
1149     recursion_loop_element = (e);                                       \
1150   }                                                                     \
1151                                                                         \
1152   recursion_loop_depth++;                                               \
1153 }
1154
1155 #define RECURSION_LOOP_DETECTION_END()                                  \
1156 {                                                                       \
1157   recursion_loop_depth--;                                               \
1158 }
1159
1160 static int recursion_loop_depth;
1161 static boolean recursion_loop_detected;
1162 static boolean recursion_loop_element;
1163
1164 static int map_player_action[MAX_PLAYERS];
1165
1166
1167 // ----------------------------------------------------------------------------
1168 // definition of elements that automatically change to other elements after
1169 // a specified time, eventually calling a function when changing
1170 // ----------------------------------------------------------------------------
1171
1172 // forward declaration for changer functions
1173 static void InitBuggyBase(int, int);
1174 static void WarnBuggyBase(int, int);
1175
1176 static void InitTrap(int, int);
1177 static void ActivateTrap(int, int);
1178 static void ChangeActiveTrap(int, int);
1179
1180 static void InitRobotWheel(int, int);
1181 static void RunRobotWheel(int, int);
1182 static void StopRobotWheel(int, int);
1183
1184 static void InitTimegateWheel(int, int);
1185 static void RunTimegateWheel(int, int);
1186
1187 static void InitMagicBallDelay(int, int);
1188 static void ActivateMagicBall(int, int);
1189
1190 struct ChangingElementInfo
1191 {
1192   int element;
1193   int target_element;
1194   int change_delay;
1195   void (*pre_change_function)(int x, int y);
1196   void (*change_function)(int x, int y);
1197   void (*post_change_function)(int x, int y);
1198 };
1199
1200 static struct ChangingElementInfo change_delay_list[] =
1201 {
1202   {
1203     EL_NUT_BREAKING,
1204     EL_EMERALD,
1205     6,
1206     NULL,
1207     NULL,
1208     NULL
1209   },
1210   {
1211     EL_PEARL_BREAKING,
1212     EL_EMPTY,
1213     8,
1214     NULL,
1215     NULL,
1216     NULL
1217   },
1218   {
1219     EL_EXIT_OPENING,
1220     EL_EXIT_OPEN,
1221     29,
1222     NULL,
1223     NULL,
1224     NULL
1225   },
1226   {
1227     EL_EXIT_CLOSING,
1228     EL_EXIT_CLOSED,
1229     29,
1230     NULL,
1231     NULL,
1232     NULL
1233   },
1234   {
1235     EL_STEEL_EXIT_OPENING,
1236     EL_STEEL_EXIT_OPEN,
1237     29,
1238     NULL,
1239     NULL,
1240     NULL
1241   },
1242   {
1243     EL_STEEL_EXIT_CLOSING,
1244     EL_STEEL_EXIT_CLOSED,
1245     29,
1246     NULL,
1247     NULL,
1248     NULL
1249   },
1250   {
1251     EL_EM_EXIT_OPENING,
1252     EL_EM_EXIT_OPEN,
1253     29,
1254     NULL,
1255     NULL,
1256     NULL
1257   },
1258   {
1259     EL_EM_EXIT_CLOSING,
1260     EL_EMPTY,
1261     29,
1262     NULL,
1263     NULL,
1264     NULL
1265   },
1266   {
1267     EL_EM_STEEL_EXIT_OPENING,
1268     EL_EM_STEEL_EXIT_OPEN,
1269     29,
1270     NULL,
1271     NULL,
1272     NULL
1273   },
1274   {
1275     EL_EM_STEEL_EXIT_CLOSING,
1276     EL_STEELWALL,
1277     29,
1278     NULL,
1279     NULL,
1280     NULL
1281   },
1282   {
1283     EL_SP_EXIT_OPENING,
1284     EL_SP_EXIT_OPEN,
1285     29,
1286     NULL,
1287     NULL,
1288     NULL
1289   },
1290   {
1291     EL_SP_EXIT_CLOSING,
1292     EL_SP_EXIT_CLOSED,
1293     29,
1294     NULL,
1295     NULL,
1296     NULL
1297   },
1298   {
1299     EL_SWITCHGATE_OPENING,
1300     EL_SWITCHGATE_OPEN,
1301     29,
1302     NULL,
1303     NULL,
1304     NULL
1305   },
1306   {
1307     EL_SWITCHGATE_CLOSING,
1308     EL_SWITCHGATE_CLOSED,
1309     29,
1310     NULL,
1311     NULL,
1312     NULL
1313   },
1314   {
1315     EL_TIMEGATE_OPENING,
1316     EL_TIMEGATE_OPEN,
1317     29,
1318     NULL,
1319     NULL,
1320     NULL
1321   },
1322   {
1323     EL_TIMEGATE_CLOSING,
1324     EL_TIMEGATE_CLOSED,
1325     29,
1326     NULL,
1327     NULL,
1328     NULL
1329   },
1330
1331   {
1332     EL_ACID_SPLASH_LEFT,
1333     EL_EMPTY,
1334     8,
1335     NULL,
1336     NULL,
1337     NULL
1338   },
1339   {
1340     EL_ACID_SPLASH_RIGHT,
1341     EL_EMPTY,
1342     8,
1343     NULL,
1344     NULL,
1345     NULL
1346   },
1347   {
1348     EL_SP_BUGGY_BASE,
1349     EL_SP_BUGGY_BASE_ACTIVATING,
1350     0,
1351     InitBuggyBase,
1352     NULL,
1353     NULL
1354   },
1355   {
1356     EL_SP_BUGGY_BASE_ACTIVATING,
1357     EL_SP_BUGGY_BASE_ACTIVE,
1358     0,
1359     InitBuggyBase,
1360     NULL,
1361     NULL
1362   },
1363   {
1364     EL_SP_BUGGY_BASE_ACTIVE,
1365     EL_SP_BUGGY_BASE,
1366     0,
1367     InitBuggyBase,
1368     WarnBuggyBase,
1369     NULL
1370   },
1371   {
1372     EL_TRAP,
1373     EL_TRAP_ACTIVE,
1374     0,
1375     InitTrap,
1376     NULL,
1377     ActivateTrap
1378   },
1379   {
1380     EL_TRAP_ACTIVE,
1381     EL_TRAP,
1382     31,
1383     NULL,
1384     ChangeActiveTrap,
1385     NULL
1386   },
1387   {
1388     EL_ROBOT_WHEEL_ACTIVE,
1389     EL_ROBOT_WHEEL,
1390     0,
1391     InitRobotWheel,
1392     RunRobotWheel,
1393     StopRobotWheel
1394   },
1395   {
1396     EL_TIMEGATE_SWITCH_ACTIVE,
1397     EL_TIMEGATE_SWITCH,
1398     0,
1399     InitTimegateWheel,
1400     RunTimegateWheel,
1401     NULL
1402   },
1403   {
1404     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1405     EL_DC_TIMEGATE_SWITCH,
1406     0,
1407     InitTimegateWheel,
1408     RunTimegateWheel,
1409     NULL
1410   },
1411   {
1412     EL_EMC_MAGIC_BALL_ACTIVE,
1413     EL_EMC_MAGIC_BALL_ACTIVE,
1414     0,
1415     InitMagicBallDelay,
1416     NULL,
1417     ActivateMagicBall
1418   },
1419   {
1420     EL_EMC_SPRING_BUMPER_ACTIVE,
1421     EL_EMC_SPRING_BUMPER,
1422     8,
1423     NULL,
1424     NULL,
1425     NULL
1426   },
1427   {
1428     EL_DIAGONAL_SHRINKING,
1429     EL_UNDEFINED,
1430     0,
1431     NULL,
1432     NULL,
1433     NULL
1434   },
1435   {
1436     EL_DIAGONAL_GROWING,
1437     EL_UNDEFINED,
1438     0,
1439     NULL,
1440     NULL,
1441     NULL,
1442   },
1443
1444   {
1445     EL_UNDEFINED,
1446     EL_UNDEFINED,
1447     -1,
1448     NULL,
1449     NULL,
1450     NULL
1451   }
1452 };
1453
1454 struct
1455 {
1456   int element;
1457   int push_delay_fixed, push_delay_random;
1458 }
1459 push_delay_list[] =
1460 {
1461   { EL_SPRING,                  0, 0 },
1462   { EL_BALLOON,                 0, 0 },
1463
1464   { EL_SOKOBAN_OBJECT,          2, 0 },
1465   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1466   { EL_SATELLITE,               2, 0 },
1467   { EL_SP_DISK_YELLOW,          2, 0 },
1468
1469   { EL_UNDEFINED,               0, 0 },
1470 };
1471
1472 struct
1473 {
1474   int element;
1475   int move_stepsize;
1476 }
1477 move_stepsize_list[] =
1478 {
1479   { EL_AMOEBA_DROP,             2 },
1480   { EL_AMOEBA_DROPPING,         2 },
1481   { EL_QUICKSAND_FILLING,       1 },
1482   { EL_QUICKSAND_EMPTYING,      1 },
1483   { EL_QUICKSAND_FAST_FILLING,  2 },
1484   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1485   { EL_MAGIC_WALL_FILLING,      2 },
1486   { EL_MAGIC_WALL_EMPTYING,     2 },
1487   { EL_BD_MAGIC_WALL_FILLING,   2 },
1488   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1489   { EL_DC_MAGIC_WALL_FILLING,   2 },
1490   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1491
1492   { EL_UNDEFINED,               0 },
1493 };
1494
1495 struct
1496 {
1497   int element;
1498   int count;
1499 }
1500 collect_count_list[] =
1501 {
1502   { EL_EMERALD,                 1 },
1503   { EL_BD_DIAMOND,              1 },
1504   { EL_EMERALD_YELLOW,          1 },
1505   { EL_EMERALD_RED,             1 },
1506   { EL_EMERALD_PURPLE,          1 },
1507   { EL_DIAMOND,                 3 },
1508   { EL_SP_INFOTRON,             1 },
1509   { EL_PEARL,                   5 },
1510   { EL_CRYSTAL,                 8 },
1511
1512   { EL_UNDEFINED,               0 },
1513 };
1514
1515 struct
1516 {
1517   int element;
1518   int direction;
1519 }
1520 access_direction_list[] =
1521 {
1522   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1523   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1524   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1525   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1526   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1527   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1528   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1529   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1530   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1531   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1532   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1533
1534   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1535   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1536   { EL_SP_PORT_UP,                                                   MV_DOWN },
1537   { EL_SP_PORT_DOWN,                                         MV_UP           },
1538   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1539   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1540   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1541   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1542   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1543   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1544   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1545   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1546   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1547   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1548   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1549   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1550   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1551   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1552   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1553
1554   { EL_UNDEFINED,                       MV_NONE                              }
1555 };
1556
1557 static struct XY xy_topdown[] =
1558 {
1559   {  0, -1 },
1560   { -1,  0 },
1561   { +1,  0 },
1562   {  0, +1 }
1563 };
1564
1565 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1566
1567 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1568 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1569 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Tile[x][y]) || \
1570                                  IS_JUST_CHANGING(x, y))
1571
1572 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1573
1574 // static variables for playfield scan mode (scanning forward or backward)
1575 static int playfield_scan_start_x = 0;
1576 static int playfield_scan_start_y = 0;
1577 static int playfield_scan_delta_x = 1;
1578 static int playfield_scan_delta_y = 1;
1579
1580 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1581                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1582                                      (y) += playfield_scan_delta_y)     \
1583                                 for ((x) = playfield_scan_start_x;      \
1584                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1585                                      (x) += playfield_scan_delta_x)
1586
1587 #ifdef DEBUG
1588 void DEBUG_SetMaximumDynamite(void)
1589 {
1590   int i;
1591
1592   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1593     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1594       local_player->inventory_element[local_player->inventory_size++] =
1595         EL_DYNAMITE;
1596 }
1597 #endif
1598
1599 static void InitPlayfieldScanModeVars(void)
1600 {
1601   if (game.use_reverse_scan_direction)
1602   {
1603     playfield_scan_start_x = lev_fieldx - 1;
1604     playfield_scan_start_y = lev_fieldy - 1;
1605
1606     playfield_scan_delta_x = -1;
1607     playfield_scan_delta_y = -1;
1608   }
1609   else
1610   {
1611     playfield_scan_start_x = 0;
1612     playfield_scan_start_y = 0;
1613
1614     playfield_scan_delta_x = 1;
1615     playfield_scan_delta_y = 1;
1616   }
1617 }
1618
1619 static void InitPlayfieldScanMode(int mode)
1620 {
1621   game.use_reverse_scan_direction =
1622     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1623
1624   InitPlayfieldScanModeVars();
1625 }
1626
1627 static int get_move_delay_from_stepsize(int move_stepsize)
1628 {
1629   move_stepsize =
1630     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1631
1632   // make sure that stepsize value is always a power of 2
1633   move_stepsize = (1 << log_2(move_stepsize));
1634
1635   return TILEX / move_stepsize;
1636 }
1637
1638 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1639                                boolean init_game)
1640 {
1641   int player_nr = player->index_nr;
1642   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1643   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1644
1645   // do no immediately change move delay -- the player might just be moving
1646   player->move_delay_value_next = move_delay;
1647
1648   // information if player can move must be set separately
1649   player->cannot_move = cannot_move;
1650
1651   if (init_game)
1652   {
1653     player->move_delay       = game.initial_move_delay[player_nr];
1654     player->move_delay_value = game.initial_move_delay_value[player_nr];
1655
1656     player->move_delay_value_next = -1;
1657
1658     player->move_delay_reset_counter = 0;
1659   }
1660 }
1661
1662 void GetPlayerConfig(void)
1663 {
1664   GameFrameDelay = setup.game_frame_delay;
1665
1666   if (!audio.sound_available)
1667     setup.sound_simple = FALSE;
1668
1669   if (!audio.loops_available)
1670     setup.sound_loops = FALSE;
1671
1672   if (!audio.music_available)
1673     setup.sound_music = FALSE;
1674
1675   if (!video.fullscreen_available)
1676     setup.fullscreen = FALSE;
1677
1678   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1679
1680   SetAudioMode(setup.sound);
1681 }
1682
1683 int GetElementFromGroupElement(int element)
1684 {
1685   if (IS_GROUP_ELEMENT(element))
1686   {
1687     struct ElementGroupInfo *group = element_info[element].group;
1688     int last_anim_random_frame = gfx.anim_random_frame;
1689     int element_pos;
1690
1691     if (group->choice_mode == ANIM_RANDOM)
1692       gfx.anim_random_frame = RND(group->num_elements_resolved);
1693
1694     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1695                                     group->choice_mode, 0,
1696                                     group->choice_pos);
1697
1698     if (group->choice_mode == ANIM_RANDOM)
1699       gfx.anim_random_frame = last_anim_random_frame;
1700
1701     group->choice_pos++;
1702
1703     element = group->element_resolved[element_pos];
1704   }
1705
1706   return element;
1707 }
1708
1709 static void IncrementSokobanFieldsNeeded(void)
1710 {
1711   if (level.sb_fields_needed)
1712     game.sokoban_fields_still_needed++;
1713 }
1714
1715 static void IncrementSokobanObjectsNeeded(void)
1716 {
1717   if (level.sb_objects_needed)
1718     game.sokoban_objects_still_needed++;
1719 }
1720
1721 static void DecrementSokobanFieldsNeeded(void)
1722 {
1723   if (game.sokoban_fields_still_needed > 0)
1724     game.sokoban_fields_still_needed--;
1725 }
1726
1727 static void DecrementSokobanObjectsNeeded(void)
1728 {
1729   if (game.sokoban_objects_still_needed > 0)
1730     game.sokoban_objects_still_needed--;
1731 }
1732
1733 static void InitPlayerField(int x, int y, int element, boolean init_game)
1734 {
1735   if (element == EL_SP_MURPHY)
1736   {
1737     if (init_game)
1738     {
1739       if (stored_player[0].present)
1740       {
1741         Tile[x][y] = EL_SP_MURPHY_CLONE;
1742
1743         return;
1744       }
1745       else
1746       {
1747         stored_player[0].initial_element = element;
1748         stored_player[0].use_murphy = TRUE;
1749
1750         if (!level.use_artwork_element[0])
1751           stored_player[0].artwork_element = EL_SP_MURPHY;
1752       }
1753
1754       Tile[x][y] = EL_PLAYER_1;
1755     }
1756   }
1757
1758   if (init_game)
1759   {
1760     struct PlayerInfo *player = &stored_player[Tile[x][y] - EL_PLAYER_1];
1761     int jx = player->jx, jy = player->jy;
1762
1763     player->present = TRUE;
1764
1765     player->block_last_field = (element == EL_SP_MURPHY ?
1766                                 level.sp_block_last_field :
1767                                 level.block_last_field);
1768
1769     // ---------- initialize player's last field block delay ------------------
1770
1771     // always start with reliable default value (no adjustment needed)
1772     player->block_delay_adjustment = 0;
1773
1774     // special case 1: in Supaplex, Murphy blocks last field one more frame
1775     if (player->block_last_field && element == EL_SP_MURPHY)
1776       player->block_delay_adjustment = 1;
1777
1778     // special case 2: in game engines before 3.1.1, blocking was different
1779     if (game.use_block_last_field_bug)
1780       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1781
1782     if (!network.enabled || player->connected_network)
1783     {
1784       player->active = TRUE;
1785
1786       // remove potentially duplicate players
1787       if (IN_LEV_FIELD(jx, jy) && StorePlayer[jx][jy] == Tile[x][y])
1788         StorePlayer[jx][jy] = 0;
1789
1790       StorePlayer[x][y] = Tile[x][y];
1791
1792 #if DEBUG_INIT_PLAYER
1793       Debug("game:init:player", "- player element %d activated",
1794             player->element_nr);
1795       Debug("game:init:player", "  (local player is %d and currently %s)",
1796             local_player->element_nr,
1797             local_player->active ? "active" : "not active");
1798     }
1799 #endif
1800
1801     Tile[x][y] = EL_EMPTY;
1802
1803     player->jx = player->last_jx = x;
1804     player->jy = player->last_jy = y;
1805   }
1806
1807   // always check if player was just killed and should be reanimated
1808   {
1809     int player_nr = GET_PLAYER_NR(element);
1810     struct PlayerInfo *player = &stored_player[player_nr];
1811
1812     if (player->active && player->killed)
1813       player->reanimated = TRUE; // if player was just killed, reanimate him
1814   }
1815 }
1816
1817 static void InitField(int x, int y, boolean init_game)
1818 {
1819   int element = Tile[x][y];
1820
1821   switch (element)
1822   {
1823     case EL_SP_MURPHY:
1824     case EL_PLAYER_1:
1825     case EL_PLAYER_2:
1826     case EL_PLAYER_3:
1827     case EL_PLAYER_4:
1828       InitPlayerField(x, y, element, init_game);
1829       break;
1830
1831     case EL_SOKOBAN_FIELD_PLAYER:
1832       element = Tile[x][y] = EL_PLAYER_1;
1833       InitField(x, y, init_game);
1834
1835       element = Tile[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1836       InitField(x, y, init_game);
1837       break;
1838
1839     case EL_SOKOBAN_FIELD_EMPTY:
1840       IncrementSokobanFieldsNeeded();
1841       break;
1842
1843     case EL_SOKOBAN_OBJECT:
1844       IncrementSokobanObjectsNeeded();
1845       break;
1846
1847     case EL_STONEBLOCK:
1848       if (x < lev_fieldx - 1 && Tile[x + 1][y] == EL_ACID)
1849         Tile[x][y] = EL_ACID_POOL_TOPLEFT;
1850       else if (x > 0 && Tile[x - 1][y] == EL_ACID)
1851         Tile[x][y] = EL_ACID_POOL_TOPRIGHT;
1852       else if (y > 0 && Tile[x][y - 1] == EL_ACID_POOL_TOPLEFT)
1853         Tile[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1854       else if (y > 0 && Tile[x][y - 1] == EL_ACID)
1855         Tile[x][y] = EL_ACID_POOL_BOTTOM;
1856       else if (y > 0 && Tile[x][y - 1] == EL_ACID_POOL_TOPRIGHT)
1857         Tile[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1858       break;
1859
1860     case EL_BUG:
1861     case EL_BUG_RIGHT:
1862     case EL_BUG_UP:
1863     case EL_BUG_LEFT:
1864     case EL_BUG_DOWN:
1865     case EL_SPACESHIP:
1866     case EL_SPACESHIP_RIGHT:
1867     case EL_SPACESHIP_UP:
1868     case EL_SPACESHIP_LEFT:
1869     case EL_SPACESHIP_DOWN:
1870     case EL_BD_BUTTERFLY:
1871     case EL_BD_BUTTERFLY_RIGHT:
1872     case EL_BD_BUTTERFLY_UP:
1873     case EL_BD_BUTTERFLY_LEFT:
1874     case EL_BD_BUTTERFLY_DOWN:
1875     case EL_BD_FIREFLY:
1876     case EL_BD_FIREFLY_RIGHT:
1877     case EL_BD_FIREFLY_UP:
1878     case EL_BD_FIREFLY_LEFT:
1879     case EL_BD_FIREFLY_DOWN:
1880     case EL_PACMAN_RIGHT:
1881     case EL_PACMAN_UP:
1882     case EL_PACMAN_LEFT:
1883     case EL_PACMAN_DOWN:
1884     case EL_YAMYAM:
1885     case EL_YAMYAM_LEFT:
1886     case EL_YAMYAM_RIGHT:
1887     case EL_YAMYAM_UP:
1888     case EL_YAMYAM_DOWN:
1889     case EL_DARK_YAMYAM:
1890     case EL_ROBOT:
1891     case EL_PACMAN:
1892     case EL_SP_SNIKSNAK:
1893     case EL_SP_ELECTRON:
1894     case EL_MOLE:
1895     case EL_MOLE_LEFT:
1896     case EL_MOLE_RIGHT:
1897     case EL_MOLE_UP:
1898     case EL_MOLE_DOWN:
1899     case EL_SPRING_LEFT:
1900     case EL_SPRING_RIGHT:
1901       InitMovDir(x, y);
1902       break;
1903
1904     case EL_AMOEBA_FULL:
1905     case EL_BD_AMOEBA:
1906       InitAmoebaNr(x, y);
1907       break;
1908
1909     case EL_AMOEBA_DROP:
1910       if (y == lev_fieldy - 1)
1911       {
1912         Tile[x][y] = EL_AMOEBA_GROWING;
1913         Store[x][y] = EL_AMOEBA_WET;
1914       }
1915       break;
1916
1917     case EL_DYNAMITE_ACTIVE:
1918     case EL_SP_DISK_RED_ACTIVE:
1919     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1920     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1921     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1922     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1923       MovDelay[x][y] = 96;
1924       break;
1925
1926     case EL_EM_DYNAMITE_ACTIVE:
1927       MovDelay[x][y] = 32;
1928       break;
1929
1930     case EL_LAMP:
1931       game.lights_still_needed++;
1932       break;
1933
1934     case EL_PENGUIN:
1935       game.friends_still_needed++;
1936       break;
1937
1938     case EL_PIG:
1939     case EL_DRAGON:
1940       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1941       break;
1942
1943     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1944     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1945     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1946     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1947     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1948     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1949     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1950     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1951     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1952     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1953     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1954     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1955       if (init_game)
1956       {
1957         int belt_nr = getBeltNrFromBeltSwitchElement(Tile[x][y]);
1958         int belt_dir = getBeltDirFromBeltSwitchElement(Tile[x][y]);
1959         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Tile[x][y]);
1960
1961         if (game.belt_dir_nr[belt_nr] == 3)     // initial value
1962         {
1963           game.belt_dir[belt_nr] = belt_dir;
1964           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1965         }
1966         else    // more than one switch -- set it like the first switch
1967         {
1968           Tile[x][y] = Tile[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1969         }
1970       }
1971       break;
1972
1973     case EL_LIGHT_SWITCH_ACTIVE:
1974       if (init_game)
1975         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1976       break;
1977
1978     case EL_INVISIBLE_STEELWALL:
1979     case EL_INVISIBLE_WALL:
1980     case EL_INVISIBLE_SAND:
1981       if (game.light_time_left > 0 ||
1982           game.lenses_time_left > 0)
1983         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
1984       break;
1985
1986     case EL_EMC_MAGIC_BALL:
1987       if (game.ball_active)
1988         Tile[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1989       break;
1990
1991     case EL_EMC_MAGIC_BALL_SWITCH:
1992       if (game.ball_active)
1993         Tile[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1994       break;
1995
1996     case EL_TRIGGER_PLAYER:
1997     case EL_TRIGGER_ELEMENT:
1998     case EL_TRIGGER_CE_VALUE:
1999     case EL_TRIGGER_CE_SCORE:
2000     case EL_SELF:
2001     case EL_ANY_ELEMENT:
2002     case EL_CURRENT_CE_VALUE:
2003     case EL_CURRENT_CE_SCORE:
2004     case EL_PREV_CE_1:
2005     case EL_PREV_CE_2:
2006     case EL_PREV_CE_3:
2007     case EL_PREV_CE_4:
2008     case EL_PREV_CE_5:
2009     case EL_PREV_CE_6:
2010     case EL_PREV_CE_7:
2011     case EL_PREV_CE_8:
2012     case EL_NEXT_CE_1:
2013     case EL_NEXT_CE_2:
2014     case EL_NEXT_CE_3:
2015     case EL_NEXT_CE_4:
2016     case EL_NEXT_CE_5:
2017     case EL_NEXT_CE_6:
2018     case EL_NEXT_CE_7:
2019     case EL_NEXT_CE_8:
2020       // reference elements should not be used on the playfield
2021       Tile[x][y] = EL_EMPTY;
2022       break;
2023
2024     default:
2025       if (IS_CUSTOM_ELEMENT(element))
2026       {
2027         if (CAN_MOVE(element))
2028           InitMovDir(x, y);
2029
2030         if (!element_info[element].use_last_ce_value || init_game)
2031           CustomValue[x][y] = GET_NEW_CE_VALUE(Tile[x][y]);
2032       }
2033       else if (IS_GROUP_ELEMENT(element))
2034       {
2035         Tile[x][y] = GetElementFromGroupElement(element);
2036
2037         InitField(x, y, init_game);
2038       }
2039       else if (IS_EMPTY_ELEMENT(element))
2040       {
2041         GfxElementEmpty[x][y] = element;
2042         Tile[x][y] = EL_EMPTY;
2043
2044         if (element_info[element].use_gfx_element)
2045           game.use_masked_elements = TRUE;
2046       }
2047
2048       break;
2049   }
2050
2051   if (!init_game)
2052     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2053 }
2054
2055 static void InitField_WithBug1(int x, int y, boolean init_game)
2056 {
2057   InitField(x, y, init_game);
2058
2059   // not needed to call InitMovDir() -- already done by InitField()!
2060   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2061       CAN_MOVE(Tile[x][y]))
2062     InitMovDir(x, y);
2063 }
2064
2065 static void InitField_WithBug2(int x, int y, boolean init_game)
2066 {
2067   int old_element = Tile[x][y];
2068
2069   InitField(x, y, init_game);
2070
2071   // not needed to call InitMovDir() -- already done by InitField()!
2072   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2073       CAN_MOVE(old_element) &&
2074       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2075     InitMovDir(x, y);
2076
2077   /* this case is in fact a combination of not less than three bugs:
2078      first, it calls InitMovDir() for elements that can move, although this is
2079      already done by InitField(); then, it checks the element that was at this
2080      field _before_ the call to InitField() (which can change it); lastly, it
2081      was not called for "mole with direction" elements, which were treated as
2082      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2083   */
2084 }
2085
2086 static int get_key_element_from_nr(int key_nr)
2087 {
2088   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2089                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2090                           EL_EM_KEY_1 : EL_KEY_1);
2091
2092   return key_base_element + key_nr;
2093 }
2094
2095 static int get_next_dropped_element(struct PlayerInfo *player)
2096 {
2097   return (player->inventory_size > 0 ?
2098           player->inventory_element[player->inventory_size - 1] :
2099           player->inventory_infinite_element != EL_UNDEFINED ?
2100           player->inventory_infinite_element :
2101           player->dynabombs_left > 0 ?
2102           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2103           EL_UNDEFINED);
2104 }
2105
2106 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2107 {
2108   // pos >= 0: get element from bottom of the stack;
2109   // pos <  0: get element from top of the stack
2110
2111   if (pos < 0)
2112   {
2113     int min_inventory_size = -pos;
2114     int inventory_pos = player->inventory_size - min_inventory_size;
2115     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2116
2117     return (player->inventory_size >= min_inventory_size ?
2118             player->inventory_element[inventory_pos] :
2119             player->inventory_infinite_element != EL_UNDEFINED ?
2120             player->inventory_infinite_element :
2121             player->dynabombs_left >= min_dynabombs_left ?
2122             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2123             EL_UNDEFINED);
2124   }
2125   else
2126   {
2127     int min_dynabombs_left = pos + 1;
2128     int min_inventory_size = pos + 1 - player->dynabombs_left;
2129     int inventory_pos = pos - player->dynabombs_left;
2130
2131     return (player->inventory_infinite_element != EL_UNDEFINED ?
2132             player->inventory_infinite_element :
2133             player->dynabombs_left >= min_dynabombs_left ?
2134             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2135             player->inventory_size >= min_inventory_size ?
2136             player->inventory_element[inventory_pos] :
2137             EL_UNDEFINED);
2138   }
2139 }
2140
2141 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2142 {
2143   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2144   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2145   int compare_result;
2146
2147   if (gpo1->sort_priority != gpo2->sort_priority)
2148     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2149   else
2150     compare_result = gpo1->nr - gpo2->nr;
2151
2152   return compare_result;
2153 }
2154
2155 int getPlayerInventorySize(int player_nr)
2156 {
2157   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2158     return game_em.ply[player_nr]->dynamite;
2159   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2160     return game_sp.red_disk_count;
2161   else
2162     return stored_player[player_nr].inventory_size;
2163 }
2164
2165 static void InitGameControlValues(void)
2166 {
2167   int i;
2168
2169   for (i = 0; game_panel_controls[i].nr != -1; i++)
2170   {
2171     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2172     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2173     struct TextPosInfo *pos = gpc->pos;
2174     int nr = gpc->nr;
2175     int type = gpc->type;
2176
2177     if (nr != i)
2178     {
2179       Error("'game_panel_controls' structure corrupted at %d", i);
2180
2181       Fail("this should not happen -- please debug");
2182     }
2183
2184     // force update of game controls after initialization
2185     gpc->value = gpc->last_value = -1;
2186     gpc->frame = gpc->last_frame = -1;
2187     gpc->gfx_frame = -1;
2188
2189     // determine panel value width for later calculation of alignment
2190     if (type == TYPE_INTEGER || type == TYPE_STRING)
2191     {
2192       pos->width = pos->size * getFontWidth(pos->font);
2193       pos->height = getFontHeight(pos->font);
2194     }
2195     else if (type == TYPE_ELEMENT)
2196     {
2197       pos->width = pos->size;
2198       pos->height = pos->size;
2199     }
2200
2201     // fill structure for game panel draw order
2202     gpo->nr = gpc->nr;
2203     gpo->sort_priority = pos->sort_priority;
2204   }
2205
2206   // sort game panel controls according to sort_priority and control number
2207   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2208         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2209 }
2210
2211 static void UpdatePlayfieldElementCount(void)
2212 {
2213   boolean use_element_count = FALSE;
2214   int i, j, x, y;
2215
2216   // first check if it is needed at all to calculate playfield element count
2217   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2218     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2219       use_element_count = TRUE;
2220
2221   if (!use_element_count)
2222     return;
2223
2224   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2225     element_info[i].element_count = 0;
2226
2227   SCAN_PLAYFIELD(x, y)
2228   {
2229     element_info[Tile[x][y]].element_count++;
2230   }
2231
2232   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2233     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2234       if (IS_IN_GROUP(j, i))
2235         element_info[EL_GROUP_START + i].element_count +=
2236           element_info[j].element_count;
2237 }
2238
2239 static void UpdateGameControlValues(void)
2240 {
2241   int i, k;
2242   int time = (game.LevelSolved ?
2243               game.LevelSolved_CountingTime :
2244               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2245               game_em.lev->time :
2246               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2247               game_sp.time_played :
2248               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2249               game_mm.energy_left :
2250               game.no_level_time_limit ? TimePlayed : TimeLeft);
2251   int score = (game.LevelSolved ?
2252                game.LevelSolved_CountingScore :
2253                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2254                game_em.lev->score :
2255                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2256                game_sp.score :
2257                level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2258                game_mm.score :
2259                game.score);
2260   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2261               game_em.lev->gems_needed :
2262               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2263               game_sp.infotrons_still_needed :
2264               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2265               game_mm.kettles_still_needed :
2266               game.gems_still_needed);
2267   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2268                      game_em.lev->gems_needed > 0 :
2269                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2270                      game_sp.infotrons_still_needed > 0 :
2271                      level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2272                      game_mm.kettles_still_needed > 0 ||
2273                      game_mm.lights_still_needed > 0 :
2274                      game.gems_still_needed > 0 ||
2275                      game.sokoban_fields_still_needed > 0 ||
2276                      game.sokoban_objects_still_needed > 0 ||
2277                      game.lights_still_needed > 0);
2278   int health = (game.LevelSolved ?
2279                 game.LevelSolved_CountingHealth :
2280                 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2281                 MM_HEALTH(game_mm.laser_overload_value) :
2282                 game.health);
2283   int sync_random_frame = INIT_GFX_RANDOM();    // random, but synchronized
2284
2285   UpdatePlayfieldElementCount();
2286
2287   // update game panel control values
2288
2289   // used instead of "level_nr" (for network games)
2290   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = levelset.level_nr;
2291   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2292
2293   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2294   for (i = 0; i < MAX_NUM_KEYS; i++)
2295     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2296   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2297   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2298
2299   if (game.centered_player_nr == -1)
2300   {
2301     for (i = 0; i < MAX_PLAYERS; i++)
2302     {
2303       // only one player in Supaplex game engine
2304       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2305         break;
2306
2307       for (k = 0; k < MAX_NUM_KEYS; k++)
2308       {
2309         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2310         {
2311           if (game_em.ply[i]->keys & (1 << k))
2312             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2313               get_key_element_from_nr(k);
2314         }
2315         else if (stored_player[i].key[k])
2316           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2317             get_key_element_from_nr(k);
2318       }
2319
2320       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2321         getPlayerInventorySize(i);
2322
2323       if (stored_player[i].num_white_keys > 0)
2324         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2325           EL_DC_KEY_WHITE;
2326
2327       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2328         stored_player[i].num_white_keys;
2329     }
2330   }
2331   else
2332   {
2333     int player_nr = game.centered_player_nr;
2334
2335     for (k = 0; k < MAX_NUM_KEYS; k++)
2336     {
2337       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2338       {
2339         if (game_em.ply[player_nr]->keys & (1 << k))
2340           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2341             get_key_element_from_nr(k);
2342       }
2343       else if (stored_player[player_nr].key[k])
2344         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2345           get_key_element_from_nr(k);
2346     }
2347
2348     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2349       getPlayerInventorySize(player_nr);
2350
2351     if (stored_player[player_nr].num_white_keys > 0)
2352       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2353
2354     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2355       stored_player[player_nr].num_white_keys;
2356   }
2357
2358   // re-arrange keys on game panel, if needed or if defined by style settings
2359   for (i = 0; i < MAX_NUM_KEYS + 1; i++)        // all normal keys + white key
2360   {
2361     int nr = GAME_PANEL_KEY_1 + i;
2362     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2363     struct TextPosInfo *pos = gpc->pos;
2364
2365     // skip check if key is not in the player's inventory
2366     if (gpc->value == EL_EMPTY)
2367       continue;
2368
2369     // check if keys should be arranged on panel from left to right
2370     if (pos->style == STYLE_LEFTMOST_POSITION)
2371     {
2372       // check previous key positions (left from current key)
2373       for (k = 0; k < i; k++)
2374       {
2375         int nr_new = GAME_PANEL_KEY_1 + k;
2376
2377         if (game_panel_controls[nr_new].value == EL_EMPTY)
2378         {
2379           game_panel_controls[nr_new].value = gpc->value;
2380           gpc->value = EL_EMPTY;
2381
2382           break;
2383         }
2384       }
2385     }
2386
2387     // check if "undefined" keys can be placed at some other position
2388     if (pos->x == -1 && pos->y == -1)
2389     {
2390       int nr_new = GAME_PANEL_KEY_1 + i % STD_NUM_KEYS;
2391
2392       // 1st try: display key at the same position as normal or EM keys
2393       if (game_panel_controls[nr_new].value == EL_EMPTY)
2394       {
2395         game_panel_controls[nr_new].value = gpc->value;
2396       }
2397       else
2398       {
2399         // 2nd try: display key at the next free position in the key panel
2400         for (k = 0; k < STD_NUM_KEYS; k++)
2401         {
2402           nr_new = GAME_PANEL_KEY_1 + k;
2403
2404           if (game_panel_controls[nr_new].value == EL_EMPTY)
2405           {
2406             game_panel_controls[nr_new].value = gpc->value;
2407
2408             break;
2409           }
2410         }
2411       }
2412     }
2413   }
2414
2415   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2416   {
2417     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2418       get_inventory_element_from_pos(local_player, i);
2419     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2420       get_inventory_element_from_pos(local_player, -i - 1);
2421   }
2422
2423   game_panel_controls[GAME_PANEL_SCORE].value = score;
2424   game_panel_controls[GAME_PANEL_HIGHSCORE].value = scores.entry[0].score;
2425
2426   game_panel_controls[GAME_PANEL_TIME].value = time;
2427
2428   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2429   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2430   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2431
2432   if (level.time == 0)
2433     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2434   else
2435     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2436
2437   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2438   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2439
2440   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2441
2442   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2443     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2444      EL_EMPTY);
2445   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2446     local_player->shield_normal_time_left;
2447   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2448     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2449      EL_EMPTY);
2450   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2451     local_player->shield_deadly_time_left;
2452
2453   game_panel_controls[GAME_PANEL_EXIT].value =
2454     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2455
2456   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2457     (game.ball_active ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2458   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2459     (game.ball_active ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2460      EL_EMC_MAGIC_BALL_SWITCH);
2461
2462   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2463     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2464   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2465     game.light_time_left;
2466
2467   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2468     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2469   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2470     game.timegate_time_left;
2471
2472   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2473     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2474
2475   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2476     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2477   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2478     game.lenses_time_left;
2479
2480   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2481     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2482   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2483     game.magnify_time_left;
2484
2485   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2486     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2487      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2488      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2489      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2490      EL_BALLOON_SWITCH_NONE);
2491
2492   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2493     local_player->dynabomb_count;
2494   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2495     local_player->dynabomb_size;
2496   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2497     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2498
2499   game_panel_controls[GAME_PANEL_PENGUINS].value =
2500     game.friends_still_needed;
2501
2502   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2503     game.sokoban_objects_still_needed;
2504   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2505     game.sokoban_fields_still_needed;
2506
2507   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2508     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2509
2510   for (i = 0; i < NUM_BELTS; i++)
2511   {
2512     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2513       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2514        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2515     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2516       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2517   }
2518
2519   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2520     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2521   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2522     game.magic_wall_time_left;
2523
2524   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2525     local_player->gravity;
2526
2527   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2528     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2529
2530   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2531     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2532       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2533        game.panel.element[i].id : EL_UNDEFINED);
2534
2535   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2536     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2537       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2538        element_info[game.panel.element_count[i].id].element_count : 0);
2539
2540   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2541     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2542       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2543        element_info[game.panel.ce_score[i].id].collect_score : 0);
2544
2545   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2546     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2547       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2548        element_info[game.panel.ce_score_element[i].id].collect_score :
2549        EL_UNDEFINED);
2550
2551   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2552   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2553   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2554
2555   // update game panel control frames
2556
2557   for (i = 0; game_panel_controls[i].nr != -1; i++)
2558   {
2559     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2560
2561     if (gpc->type == TYPE_ELEMENT)
2562     {
2563       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2564       {
2565         int last_anim_random_frame = gfx.anim_random_frame;
2566         int element = gpc->value;
2567         int graphic = el2panelimg(element);
2568         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2569                                sync_random_frame :
2570                                graphic_info[graphic].anim_global_anim_sync ?
2571                                getGlobalAnimSyncFrame() : INIT_GFX_RANDOM());
2572
2573         if (gpc->value != gpc->last_value)
2574         {
2575           gpc->gfx_frame = 0;
2576           gpc->gfx_random = init_gfx_random;
2577         }
2578         else
2579         {
2580           gpc->gfx_frame++;
2581
2582           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2583               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2584             gpc->gfx_random = init_gfx_random;
2585         }
2586
2587         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2588           gfx.anim_random_frame = gpc->gfx_random;
2589
2590         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2591           gpc->gfx_frame = element_info[element].collect_score;
2592
2593         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2594
2595         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2596           gfx.anim_random_frame = last_anim_random_frame;
2597       }
2598     }
2599     else if (gpc->type == TYPE_GRAPHIC)
2600     {
2601       if (gpc->graphic != IMG_UNDEFINED)
2602       {
2603         int last_anim_random_frame = gfx.anim_random_frame;
2604         int graphic = gpc->graphic;
2605         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2606                                sync_random_frame :
2607                                graphic_info[graphic].anim_global_anim_sync ?
2608                                getGlobalAnimSyncFrame() : INIT_GFX_RANDOM());
2609
2610         if (gpc->value != gpc->last_value)
2611         {
2612           gpc->gfx_frame = 0;
2613           gpc->gfx_random = init_gfx_random;
2614         }
2615         else
2616         {
2617           gpc->gfx_frame++;
2618
2619           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2620               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2621             gpc->gfx_random = init_gfx_random;
2622         }
2623
2624         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2625           gfx.anim_random_frame = gpc->gfx_random;
2626
2627         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2628
2629         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2630           gfx.anim_random_frame = last_anim_random_frame;
2631       }
2632     }
2633   }
2634 }
2635
2636 static void DisplayGameControlValues(void)
2637 {
2638   boolean redraw_panel = FALSE;
2639   int i;
2640
2641   for (i = 0; game_panel_controls[i].nr != -1; i++)
2642   {
2643     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2644
2645     if (PANEL_DEACTIVATED(gpc->pos))
2646       continue;
2647
2648     if (gpc->value == gpc->last_value &&
2649         gpc->frame == gpc->last_frame)
2650       continue;
2651
2652     redraw_panel = TRUE;
2653   }
2654
2655   if (!redraw_panel)
2656     return;
2657
2658   // copy default game door content to main double buffer
2659
2660   // !!! CHECK AGAIN !!!
2661   SetPanelBackground();
2662   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2663   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2664
2665   // redraw game control buttons
2666   RedrawGameButtons();
2667
2668   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2669
2670   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2671   {
2672     int nr = game_panel_order[i].nr;
2673     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2674     struct TextPosInfo *pos = gpc->pos;
2675     int type = gpc->type;
2676     int value = gpc->value;
2677     int frame = gpc->frame;
2678     int size = pos->size;
2679     int font = pos->font;
2680     boolean draw_masked = pos->draw_masked;
2681     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2682
2683     if (PANEL_DEACTIVATED(pos))
2684       continue;
2685
2686     if (pos->class == get_hash_from_key("extra_panel_items") &&
2687         !setup.prefer_extra_panel_items)
2688       continue;
2689
2690     gpc->last_value = value;
2691     gpc->last_frame = frame;
2692
2693     if (type == TYPE_INTEGER)
2694     {
2695       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2696           nr == GAME_PANEL_INVENTORY_COUNT ||
2697           nr == GAME_PANEL_SCORE ||
2698           nr == GAME_PANEL_HIGHSCORE ||
2699           nr == GAME_PANEL_TIME)
2700       {
2701         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2702
2703         if (use_dynamic_size)           // use dynamic number of digits
2704         {
2705           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 :
2706                               nr == GAME_PANEL_INVENTORY_COUNT ||
2707                               nr == GAME_PANEL_TIME ? 1000 : 100000);
2708           int size_add = (nr == GAME_PANEL_LEVEL_NUMBER ||
2709                           nr == GAME_PANEL_INVENTORY_COUNT ||
2710                           nr == GAME_PANEL_TIME ? 1 : 2);
2711           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 :
2712                        nr == GAME_PANEL_INVENTORY_COUNT ||
2713                        nr == GAME_PANEL_TIME ? 3 : 5);
2714           int size2 = size1 + size_add;
2715           int font1 = pos->font;
2716           int font2 = pos->font_alt;
2717
2718           size = (value < value_change ? size1 : size2);
2719           font = (value < value_change ? font1 : font2);
2720         }
2721       }
2722
2723       // correct text size if "digits" is zero or less
2724       if (size <= 0)
2725         size = strlen(int2str(value, size));
2726
2727       // dynamically correct text alignment
2728       pos->width = size * getFontWidth(font);
2729
2730       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2731                   int2str(value, size), font, mask_mode);
2732     }
2733     else if (type == TYPE_ELEMENT)
2734     {
2735       int element, graphic;
2736       Bitmap *src_bitmap;
2737       int src_x, src_y;
2738       int width, height;
2739       int dst_x = PANEL_XPOS(pos);
2740       int dst_y = PANEL_YPOS(pos);
2741
2742       if (value != EL_UNDEFINED && value != EL_EMPTY)
2743       {
2744         element = value;
2745         graphic = el2panelimg(value);
2746
2747 #if 0
2748         Debug("game:DisplayGameControlValues", "%d, '%s' [%d]",
2749               element, EL_NAME(element), size);
2750 #endif
2751
2752         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2753           size = TILESIZE;
2754
2755         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2756                               &src_x, &src_y);
2757
2758         width  = graphic_info[graphic].width  * size / TILESIZE;
2759         height = graphic_info[graphic].height * size / TILESIZE;
2760
2761         if (draw_masked)
2762           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2763                            dst_x, dst_y);
2764         else
2765           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2766                      dst_x, dst_y);
2767       }
2768     }
2769     else if (type == TYPE_GRAPHIC)
2770     {
2771       int graphic        = gpc->graphic;
2772       int graphic_active = gpc->graphic_active;
2773       Bitmap *src_bitmap;
2774       int src_x, src_y;
2775       int width, height;
2776       int dst_x = PANEL_XPOS(pos);
2777       int dst_y = PANEL_YPOS(pos);
2778       boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2779                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2780
2781       if (graphic != IMG_UNDEFINED && !skip)
2782       {
2783         if (pos->style == STYLE_REVERSE)
2784           value = 100 - value;
2785
2786         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2787
2788         if (pos->direction & MV_HORIZONTAL)
2789         {
2790           width  = graphic_info[graphic_active].width * value / 100;
2791           height = graphic_info[graphic_active].height;
2792
2793           if (pos->direction == MV_LEFT)
2794           {
2795             src_x += graphic_info[graphic_active].width - width;
2796             dst_x += graphic_info[graphic_active].width - width;
2797           }
2798         }
2799         else
2800         {
2801           width  = graphic_info[graphic_active].width;
2802           height = graphic_info[graphic_active].height * value / 100;
2803
2804           if (pos->direction == MV_UP)
2805           {
2806             src_y += graphic_info[graphic_active].height - height;
2807             dst_y += graphic_info[graphic_active].height - height;
2808           }
2809         }
2810
2811         if (draw_masked)
2812           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2813                            dst_x, dst_y);
2814         else
2815           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2816                      dst_x, dst_y);
2817
2818         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2819
2820         if (pos->direction & MV_HORIZONTAL)
2821         {
2822           if (pos->direction == MV_RIGHT)
2823           {
2824             src_x += width;
2825             dst_x += width;
2826           }
2827           else
2828           {
2829             dst_x = PANEL_XPOS(pos);
2830           }
2831
2832           width = graphic_info[graphic].width - width;
2833         }
2834         else
2835         {
2836           if (pos->direction == MV_DOWN)
2837           {
2838             src_y += height;
2839             dst_y += height;
2840           }
2841           else
2842           {
2843             dst_y = PANEL_YPOS(pos);
2844           }
2845
2846           height = graphic_info[graphic].height - height;
2847         }
2848
2849         if (draw_masked)
2850           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2851                            dst_x, dst_y);
2852         else
2853           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2854                      dst_x, dst_y);
2855       }
2856     }
2857     else if (type == TYPE_STRING)
2858     {
2859       boolean active = (value != 0);
2860       char *state_normal = "off";
2861       char *state_active = "on";
2862       char *state = (active ? state_active : state_normal);
2863       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2864                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2865                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2866                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2867
2868       if (nr == GAME_PANEL_GRAVITY_STATE)
2869       {
2870         int font1 = pos->font;          // (used for normal state)
2871         int font2 = pos->font_alt;      // (used for active state)
2872
2873         font = (active ? font2 : font1);
2874       }
2875
2876       if (s != NULL)
2877       {
2878         char *s_cut;
2879
2880         if (size <= 0)
2881         {
2882           // don't truncate output if "chars" is zero or less
2883           size = strlen(s);
2884
2885           // dynamically correct text alignment
2886           pos->width = size * getFontWidth(font);
2887         }
2888
2889         s_cut = getStringCopyN(s, size);
2890
2891         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2892                     s_cut, font, mask_mode);
2893
2894         free(s_cut);
2895       }
2896     }
2897
2898     redraw_mask |= REDRAW_DOOR_1;
2899   }
2900
2901   SetGameStatus(GAME_MODE_PLAYING);
2902 }
2903
2904 void UpdateAndDisplayGameControlValues(void)
2905 {
2906   if (tape.deactivate_display)
2907     return;
2908
2909   UpdateGameControlValues();
2910   DisplayGameControlValues();
2911 }
2912
2913 void UpdateGameDoorValues(void)
2914 {
2915   UpdateGameControlValues();
2916 }
2917
2918 void DrawGameDoorValues(void)
2919 {
2920   DisplayGameControlValues();
2921 }
2922
2923
2924 // ============================================================================
2925 // InitGameEngine()
2926 // ----------------------------------------------------------------------------
2927 // initialize game engine due to level / tape version number
2928 // ============================================================================
2929
2930 static void InitGameEngine(void)
2931 {
2932   int i, j, k, l, x, y;
2933
2934   // set game engine from tape file when re-playing, else from level file
2935   game.engine_version = (tape.playing ? tape.engine_version :
2936                          level.game_version);
2937
2938   // set single or multi-player game mode (needed for re-playing tapes)
2939   game.team_mode = setup.team_mode;
2940
2941   if (tape.playing)
2942   {
2943     int num_players = 0;
2944
2945     for (i = 0; i < MAX_PLAYERS; i++)
2946       if (tape.player_participates[i])
2947         num_players++;
2948
2949     // multi-player tapes contain input data for more than one player
2950     game.team_mode = (num_players > 1);
2951   }
2952
2953 #if 0
2954   Debug("game:init:level", "level %d: level.game_version  == %06d", level_nr,
2955         level.game_version);
2956   Debug("game:init:level", "          tape.file_version   == %06d",
2957         tape.file_version);
2958   Debug("game:init:level", "          tape.game_version   == %06d",
2959         tape.game_version);
2960   Debug("game:init:level", "          tape.engine_version == %06d",
2961         tape.engine_version);
2962   Debug("game:init:level", "       => game.engine_version == %06d [tape mode: %s]",
2963         game.engine_version, (tape.playing ? "PLAYING" : "RECORDING"));
2964 #endif
2965
2966   // --------------------------------------------------------------------------
2967   // set flags for bugs and changes according to active game engine version
2968   // --------------------------------------------------------------------------
2969
2970   /*
2971     Summary of bugfix:
2972     Fixed property "can fall" for run-time element "EL_AMOEBA_DROPPING"
2973
2974     Bug was introduced in version:
2975     2.0.1
2976
2977     Bug was fixed in version:
2978     4.2.0.0
2979
2980     Description:
2981     In version 2.0.1, a new run-time element "EL_AMOEBA_DROPPING" was added,
2982     but the property "can fall" was missing, which caused some levels to be
2983     unsolvable. This was fixed in version 4.2.0.0.
2984
2985     Affected levels/tapes:
2986     An example for a tape that was fixed by this bugfix is tape 029 from the
2987     level set "rnd_sam_bateman".
2988     The wrong behaviour will still be used for all levels or tapes that were
2989     created/recorded with it. An example for this is tape 023 from the level
2990     set "rnd_gerhard_haeusler", which was recorded with a buggy game engine.
2991   */
2992
2993   boolean use_amoeba_dropping_cannot_fall_bug =
2994     ((game.engine_version >= VERSION_IDENT(2,0,1,0) &&
2995       game.engine_version <  VERSION_IDENT(4,2,0,0)) ||
2996      (tape.playing &&
2997       tape.game_version >= VERSION_IDENT(2,0,1,0) &&
2998       tape.game_version <  VERSION_IDENT(4,2,0,0)));
2999
3000   /*
3001     Summary of bugfix/change:
3002     Fixed move speed of elements entering or leaving magic wall.
3003
3004     Fixed/changed in version:
3005     2.0.1
3006
3007     Description:
3008     Before 2.0.1, move speed of elements entering or leaving magic wall was
3009     twice as fast as it is now.
3010     Since 2.0.1, this is set to a lower value by using move_stepsize_list[].
3011
3012     Affected levels/tapes:
3013     The first condition is generally needed for all levels/tapes before version
3014     2.0.1, which might use the old behaviour before it was changed; known tapes
3015     that are affected: Tape 014 from the level set "rnd_conor_mancone".
3016     The second condition is an exception from the above case and is needed for
3017     the special case of tapes recorded with game (not engine!) version 2.0.1 or
3018     above, but before it was known that this change would break tapes like the
3019     above and was fixed in 4.2.0.0, so that the changed behaviour was active
3020     although the engine version while recording maybe was before 2.0.1. There
3021     are a lot of tapes that are affected by this exception, like tape 006 from
3022     the level set "rnd_conor_mancone".
3023   */
3024
3025   boolean use_old_move_stepsize_for_magic_wall =
3026     (game.engine_version < VERSION_IDENT(2,0,1,0) &&
3027      !(tape.playing &&
3028        tape.game_version >= VERSION_IDENT(2,0,1,0) &&
3029        tape.game_version <  VERSION_IDENT(4,2,0,0)));
3030
3031   /*
3032     Summary of bugfix/change:
3033     Fixed handling for custom elements that change when pushed by the player.
3034
3035     Fixed/changed in version:
3036     3.1.0
3037
3038     Description:
3039     Before 3.1.0, custom elements that "change when pushing" changed directly
3040     after the player started pushing them (until then handled in "DigField()").
3041     Since 3.1.0, these custom elements are not changed until the "pushing"
3042     move of the element is finished (now handled in "ContinueMoving()").
3043
3044     Affected levels/tapes:
3045     The first condition is generally needed for all levels/tapes before version
3046     3.1.0, which might use the old behaviour before it was changed; known tapes
3047     that are affected are some tapes from the level set "Walpurgis Gardens" by
3048     Jamie Cullen.
3049     The second condition is an exception from the above case and is needed for
3050     the special case of tapes recorded with game (not engine!) version 3.1.0 or
3051     above (including some development versions of 3.1.0), but before it was
3052     known that this change would break tapes like the above and was fixed in
3053     3.1.1, so that the changed behaviour was active although the engine version
3054     while recording maybe was before 3.1.0. There is at least one tape that is
3055     affected by this exception, which is the tape for the one-level set "Bug
3056     Machine" by Juergen Bonhagen.
3057   */
3058
3059   game.use_change_when_pushing_bug =
3060     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3061      !(tape.playing &&
3062        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3063        tape.game_version <  VERSION_IDENT(3,1,1,0)));
3064
3065   /*
3066     Summary of bugfix/change:
3067     Fixed handling for blocking the field the player leaves when moving.
3068
3069     Fixed/changed in version:
3070     3.1.1
3071
3072     Description:
3073     Before 3.1.1, when "block last field when moving" was enabled, the field
3074     the player is leaving when moving was blocked for the time of the move,
3075     and was directly unblocked afterwards. This resulted in the last field
3076     being blocked for exactly one less than the number of frames of one player
3077     move. Additionally, even when blocking was disabled, the last field was
3078     blocked for exactly one frame.
3079     Since 3.1.1, due to changes in player movement handling, the last field
3080     is not blocked at all when blocking is disabled. When blocking is enabled,
3081     the last field is blocked for exactly the number of frames of one player
3082     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3083     last field is blocked for exactly one more than the number of frames of
3084     one player move.
3085
3086     Affected levels/tapes:
3087     (!!! yet to be determined -- probably many !!!)
3088   */
3089
3090   game.use_block_last_field_bug =
3091     (game.engine_version < VERSION_IDENT(3,1,1,0));
3092
3093   /* various special flags and settings for native Emerald Mine game engine */
3094
3095   game_em.use_single_button =
3096     (game.engine_version > VERSION_IDENT(4,0,0,2));
3097
3098   game_em.use_snap_key_bug =
3099     (game.engine_version < VERSION_IDENT(4,0,1,0));
3100
3101   game_em.use_random_bug =
3102     (tape.property_bits & TAPE_PROPERTY_EM_RANDOM_BUG);
3103
3104   boolean use_old_em_engine = (game.engine_version < VERSION_IDENT(4,2,0,0));
3105
3106   game_em.use_old_explosions            = use_old_em_engine;
3107   game_em.use_old_android               = use_old_em_engine;
3108   game_em.use_old_push_elements         = use_old_em_engine;
3109   game_em.use_old_push_into_acid        = use_old_em_engine;
3110
3111   game_em.use_wrap_around               = !use_old_em_engine;
3112
3113   // --------------------------------------------------------------------------
3114
3115   // set maximal allowed number of custom element changes per game frame
3116   game.max_num_changes_per_frame = 1;
3117
3118   // default scan direction: scan playfield from top/left to bottom/right
3119   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3120
3121   // dynamically adjust element properties according to game engine version
3122   InitElementPropertiesEngine(game.engine_version);
3123
3124   // ---------- initialize special element properties -------------------------
3125
3126   // "EL_AMOEBA_DROPPING" missed property "can fall" in older game versions
3127   if (use_amoeba_dropping_cannot_fall_bug)
3128     SET_PROPERTY(EL_AMOEBA_DROPPING, EP_CAN_FALL, FALSE);
3129
3130   // ---------- initialize player's initial move delay ------------------------
3131
3132   // dynamically adjust player properties according to level information
3133   for (i = 0; i < MAX_PLAYERS; i++)
3134     game.initial_move_delay_value[i] =
3135       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3136
3137   // dynamically adjust player properties according to game engine version
3138   for (i = 0; i < MAX_PLAYERS; i++)
3139     game.initial_move_delay[i] =
3140       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3141        game.initial_move_delay_value[i] : 0);
3142
3143   // ---------- initialize player's initial push delay ------------------------
3144
3145   // dynamically adjust player properties according to game engine version
3146   game.initial_push_delay_value =
3147     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3148
3149   // ---------- initialize changing elements ----------------------------------
3150
3151   // initialize changing elements information
3152   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3153   {
3154     struct ElementInfo *ei = &element_info[i];
3155
3156     // this pointer might have been changed in the level editor
3157     ei->change = &ei->change_page[0];
3158
3159     if (!IS_CUSTOM_ELEMENT(i))
3160     {
3161       ei->change->target_element = EL_EMPTY_SPACE;
3162       ei->change->delay_fixed = 0;
3163       ei->change->delay_random = 0;
3164       ei->change->delay_frames = 1;
3165     }
3166
3167     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3168     {
3169       ei->has_change_event[j] = FALSE;
3170
3171       ei->event_page_nr[j] = 0;
3172       ei->event_page[j] = &ei->change_page[0];
3173     }
3174   }
3175
3176   // add changing elements from pre-defined list
3177   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3178   {
3179     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3180     struct ElementInfo *ei = &element_info[ch_delay->element];
3181
3182     ei->change->target_element       = ch_delay->target_element;
3183     ei->change->delay_fixed          = ch_delay->change_delay;
3184
3185     ei->change->pre_change_function  = ch_delay->pre_change_function;
3186     ei->change->change_function      = ch_delay->change_function;
3187     ei->change->post_change_function = ch_delay->post_change_function;
3188
3189     ei->change->can_change = TRUE;
3190     ei->change->can_change_or_has_action = TRUE;
3191
3192     ei->has_change_event[CE_DELAY] = TRUE;
3193
3194     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3195     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3196   }
3197
3198   // ---------- initialize internal run-time variables ------------------------
3199
3200   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3201   {
3202     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3203
3204     for (j = 0; j < ei->num_change_pages; j++)
3205     {
3206       ei->change_page[j].can_change_or_has_action =
3207         (ei->change_page[j].can_change |
3208          ei->change_page[j].has_action);
3209     }
3210   }
3211
3212   // add change events from custom element configuration
3213   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3214   {
3215     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3216
3217     for (j = 0; j < ei->num_change_pages; j++)
3218     {
3219       if (!ei->change_page[j].can_change_or_has_action)
3220         continue;
3221
3222       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3223       {
3224         // only add event page for the first page found with this event
3225         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3226         {
3227           ei->has_change_event[k] = TRUE;
3228
3229           ei->event_page_nr[k] = j;
3230           ei->event_page[k] = &ei->change_page[j];
3231         }
3232       }
3233     }
3234   }
3235
3236   // ---------- initialize reference elements in change conditions ------------
3237
3238   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3239   {
3240     int element = EL_CUSTOM_START + i;
3241     struct ElementInfo *ei = &element_info[element];
3242
3243     for (j = 0; j < ei->num_change_pages; j++)
3244     {
3245       int trigger_element = ei->change_page[j].initial_trigger_element;
3246
3247       if (trigger_element >= EL_PREV_CE_8 &&
3248           trigger_element <= EL_NEXT_CE_8)
3249         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3250
3251       ei->change_page[j].trigger_element = trigger_element;
3252     }
3253   }
3254
3255   // ---------- initialize run-time trigger player and element ----------------
3256
3257   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3258   {
3259     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3260
3261     for (j = 0; j < ei->num_change_pages; j++)
3262     {
3263       struct ElementChangeInfo *change = &ei->change_page[j];
3264
3265       change->actual_trigger_element = EL_EMPTY;
3266       change->actual_trigger_player = EL_EMPTY;
3267       change->actual_trigger_player_bits = CH_PLAYER_NONE;
3268       change->actual_trigger_side = CH_SIDE_NONE;
3269       change->actual_trigger_ce_value = 0;
3270       change->actual_trigger_ce_score = 0;
3271     }
3272   }
3273
3274   // ---------- initialize trigger events -------------------------------------
3275
3276   // initialize trigger events information
3277   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3278     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3279       trigger_events[i][j] = FALSE;
3280
3281   // add trigger events from element change event properties
3282   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3283   {
3284     struct ElementInfo *ei = &element_info[i];
3285
3286     for (j = 0; j < ei->num_change_pages; j++)
3287     {
3288       struct ElementChangeInfo *change = &ei->change_page[j];
3289
3290       if (!change->can_change_or_has_action)
3291         continue;
3292
3293       if (change->has_event[CE_BY_OTHER_ACTION])
3294       {
3295         int trigger_element = change->trigger_element;
3296
3297         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3298         {
3299           if (change->has_event[k])
3300           {
3301             if (IS_GROUP_ELEMENT(trigger_element))
3302             {
3303               struct ElementGroupInfo *group =
3304                 element_info[trigger_element].group;
3305
3306               for (l = 0; l < group->num_elements_resolved; l++)
3307                 trigger_events[group->element_resolved[l]][k] = TRUE;
3308             }
3309             else if (trigger_element == EL_ANY_ELEMENT)
3310               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3311                 trigger_events[l][k] = TRUE;
3312             else
3313               trigger_events[trigger_element][k] = TRUE;
3314           }
3315         }
3316       }
3317     }
3318   }
3319
3320   // ---------- initialize push delay -----------------------------------------
3321
3322   // initialize push delay values to default
3323   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3324   {
3325     if (!IS_CUSTOM_ELEMENT(i))
3326     {
3327       // set default push delay values (corrected since version 3.0.7-1)
3328       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3329       {
3330         element_info[i].push_delay_fixed = 2;
3331         element_info[i].push_delay_random = 8;
3332       }
3333       else
3334       {
3335         element_info[i].push_delay_fixed = 8;
3336         element_info[i].push_delay_random = 8;
3337       }
3338     }
3339   }
3340
3341   // set push delay value for certain elements from pre-defined list
3342   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3343   {
3344     int e = push_delay_list[i].element;
3345
3346     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3347     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3348   }
3349
3350   // set push delay value for Supaplex elements for newer engine versions
3351   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3352   {
3353     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3354     {
3355       if (IS_SP_ELEMENT(i))
3356       {
3357         // set SP push delay to just enough to push under a falling zonk
3358         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3359
3360         element_info[i].push_delay_fixed  = delay;
3361         element_info[i].push_delay_random = 0;
3362       }
3363     }
3364   }
3365
3366   // ---------- initialize move stepsize --------------------------------------
3367
3368   // initialize move stepsize values to default
3369   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3370     if (!IS_CUSTOM_ELEMENT(i))
3371       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3372
3373   // set move stepsize value for certain elements from pre-defined list
3374   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3375   {
3376     int e = move_stepsize_list[i].element;
3377
3378     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3379
3380     // set move stepsize value for certain elements for older engine versions
3381     if (use_old_move_stepsize_for_magic_wall)
3382     {
3383       if (e == EL_MAGIC_WALL_FILLING ||
3384           e == EL_MAGIC_WALL_EMPTYING ||
3385           e == EL_BD_MAGIC_WALL_FILLING ||
3386           e == EL_BD_MAGIC_WALL_EMPTYING)
3387         element_info[e].move_stepsize *= 2;
3388     }
3389   }
3390
3391   // ---------- initialize collect score --------------------------------------
3392
3393   // initialize collect score values for custom elements from initial value
3394   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3395     if (IS_CUSTOM_ELEMENT(i))
3396       element_info[i].collect_score = element_info[i].collect_score_initial;
3397
3398   // ---------- initialize collect count --------------------------------------
3399
3400   // initialize collect count values for non-custom elements
3401   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3402     if (!IS_CUSTOM_ELEMENT(i))
3403       element_info[i].collect_count_initial = 0;
3404
3405   // add collect count values for all elements from pre-defined list
3406   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3407     element_info[collect_count_list[i].element].collect_count_initial =
3408       collect_count_list[i].count;
3409
3410   // ---------- initialize access direction -----------------------------------
3411
3412   // initialize access direction values to default (access from every side)
3413   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3414     if (!IS_CUSTOM_ELEMENT(i))
3415       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3416
3417   // set access direction value for certain elements from pre-defined list
3418   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3419     element_info[access_direction_list[i].element].access_direction =
3420       access_direction_list[i].direction;
3421
3422   // ---------- initialize explosion content ----------------------------------
3423   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3424   {
3425     if (IS_CUSTOM_ELEMENT(i))
3426       continue;
3427
3428     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3429     {
3430       // (content for EL_YAMYAM set at run-time with game.yamyam_content_nr)
3431
3432       element_info[i].content.e[x][y] =
3433         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3434          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3435          i == EL_PLAYER_3 ? EL_EMERALD :
3436          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3437          i == EL_MOLE ? EL_EMERALD_RED :
3438          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3439          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3440          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3441          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3442          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3443          i == EL_WALL_EMERALD ? EL_EMERALD :
3444          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3445          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3446          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3447          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3448          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3449          i == EL_WALL_PEARL ? EL_PEARL :
3450          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3451          EL_EMPTY);
3452     }
3453   }
3454
3455   // ---------- initialize recursion detection --------------------------------
3456   recursion_loop_depth = 0;
3457   recursion_loop_detected = FALSE;
3458   recursion_loop_element = EL_UNDEFINED;
3459
3460   // ---------- initialize graphics engine ------------------------------------
3461   game.scroll_delay_value =
3462     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3463      level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3464      !setup.forced_scroll_delay           ? 0 :
3465      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3466   game.scroll_delay_value =
3467     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3468
3469   // ---------- initialize game engine snapshots ------------------------------
3470   for (i = 0; i < MAX_PLAYERS; i++)
3471     game.snapshot.last_action[i] = 0;
3472   game.snapshot.changed_action = FALSE;
3473   game.snapshot.collected_item = FALSE;
3474   game.snapshot.mode =
3475     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3476      SNAPSHOT_MODE_EVERY_STEP :
3477      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3478      SNAPSHOT_MODE_EVERY_MOVE :
3479      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3480      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3481   game.snapshot.save_snapshot = FALSE;
3482
3483   // ---------- initialize level time for Supaplex engine ---------------------
3484   // Supaplex levels with time limit currently unsupported -- should be added
3485   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3486     level.time = 0;
3487
3488   // ---------- initialize flags for handling game actions --------------------
3489
3490   // set flags for game actions to default values
3491   game.use_key_actions = TRUE;
3492   game.use_mouse_actions = FALSE;
3493
3494   // when using Mirror Magic game engine, handle mouse events only
3495   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
3496   {
3497     game.use_key_actions = FALSE;
3498     game.use_mouse_actions = TRUE;
3499   }
3500
3501   // check for custom elements with mouse click events
3502   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3503   {
3504     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3505     {
3506       int element = EL_CUSTOM_START + i;
3507
3508       if (HAS_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
3509           HAS_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE) ||
3510           HAS_CHANGE_EVENT(element, CE_MOUSE_CLICKED_ON_X) ||
3511           HAS_CHANGE_EVENT(element, CE_MOUSE_PRESSED_ON_X))
3512         game.use_mouse_actions = TRUE;
3513     }
3514   }
3515 }
3516
3517 static int get_num_special_action(int element, int action_first,
3518                                   int action_last)
3519 {
3520   int num_special_action = 0;
3521   int i, j;
3522
3523   for (i = action_first; i <= action_last; i++)
3524   {
3525     boolean found = FALSE;
3526
3527     for (j = 0; j < NUM_DIRECTIONS; j++)
3528       if (el_act_dir2img(element, i, j) !=
3529           el_act_dir2img(element, ACTION_DEFAULT, j))
3530         found = TRUE;
3531
3532     if (found)
3533       num_special_action++;
3534     else
3535       break;
3536   }
3537
3538   return num_special_action;
3539 }
3540
3541
3542 // ============================================================================
3543 // InitGame()
3544 // ----------------------------------------------------------------------------
3545 // initialize and start new game
3546 // ============================================================================
3547
3548 #if DEBUG_INIT_PLAYER
3549 static void DebugPrintPlayerStatus(char *message)
3550 {
3551   int i;
3552
3553   if (!options.debug)
3554     return;
3555
3556   Debug("game:init:player", "%s:", message);
3557
3558   for (i = 0; i < MAX_PLAYERS; i++)
3559   {
3560     struct PlayerInfo *player = &stored_player[i];
3561
3562     Debug("game:init:player",
3563           "- player %d: present == %d, connected == %d [%d/%d], active == %d%s",
3564           i + 1,
3565           player->present,
3566           player->connected,
3567           player->connected_locally,
3568           player->connected_network,
3569           player->active,
3570           (local_player == player ? " (local player)" : ""));
3571   }
3572 }
3573 #endif
3574
3575 void InitGame(void)
3576 {
3577   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3578   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3579   int fade_mask = REDRAW_FIELD;
3580   boolean restarting = (game_status == GAME_MODE_PLAYING);
3581   boolean emulate_bd = TRUE;    // unless non-BOULDERDASH elements found
3582   boolean emulate_sp = TRUE;    // unless non-SUPAPLEX    elements found
3583   int initial_move_dir = MV_DOWN;
3584   int i, j, x, y;
3585
3586   // required here to update video display before fading (FIX THIS)
3587   DrawMaskedBorder(REDRAW_DOOR_2);
3588
3589   if (!game.restart_level)
3590     CloseDoor(DOOR_CLOSE_1);
3591
3592   if (restarting)
3593   {
3594     // force fading out global animations displayed during game play
3595     SetGameStatus(GAME_MODE_PSEUDO_RESTARTING);
3596   }
3597   else
3598   {
3599     SetGameStatus(GAME_MODE_PLAYING);
3600   }
3601
3602   if (level_editor_test_game)
3603     FadeSkipNextFadeOut();
3604   else
3605     FadeSetEnterScreen();
3606
3607   if (CheckFadeAll())
3608     fade_mask = REDRAW_ALL;
3609
3610   FadeLevelSoundsAndMusic();
3611
3612   ExpireSoundLoops(TRUE);
3613
3614   FadeOut(fade_mask);
3615
3616   if (restarting)
3617   {
3618     // force restarting global animations displayed during game play
3619     RestartGlobalAnimsByStatus(GAME_MODE_PSEUDO_RESTARTING);
3620
3621     SetGameStatus(GAME_MODE_PLAYING);
3622   }
3623
3624   if (level_editor_test_game)
3625     FadeSkipNextFadeIn();
3626
3627   // needed if different viewport properties defined for playing
3628   ChangeViewportPropertiesIfNeeded();
3629
3630   ClearField();
3631
3632   DrawCompleteVideoDisplay();
3633
3634   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3635
3636   InitGameEngine();
3637   InitGameControlValues();
3638
3639   if (tape.recording)
3640   {
3641     // initialize tape actions from game when recording tape
3642     tape.use_key_actions   = game.use_key_actions;
3643     tape.use_mouse_actions = game.use_mouse_actions;
3644
3645     // initialize visible playfield size when recording tape (for team mode)
3646     tape.scr_fieldx = SCR_FIELDX;
3647     tape.scr_fieldy = SCR_FIELDY;
3648   }
3649
3650   // don't play tapes over network
3651   network_playing = (network.enabled && !tape.playing);
3652
3653   for (i = 0; i < MAX_PLAYERS; i++)
3654   {
3655     struct PlayerInfo *player = &stored_player[i];
3656
3657     player->index_nr = i;
3658     player->index_bit = (1 << i);
3659     player->element_nr = EL_PLAYER_1 + i;
3660
3661     player->present = FALSE;
3662     player->active = FALSE;
3663     player->mapped = FALSE;
3664
3665     player->killed = FALSE;
3666     player->reanimated = FALSE;
3667     player->buried = FALSE;
3668
3669     player->action = 0;
3670     player->effective_action = 0;
3671     player->programmed_action = 0;
3672     player->snap_action = 0;
3673
3674     player->mouse_action.lx = 0;
3675     player->mouse_action.ly = 0;
3676     player->mouse_action.button = 0;
3677     player->mouse_action.button_hint = 0;
3678
3679     player->effective_mouse_action.lx = 0;
3680     player->effective_mouse_action.ly = 0;
3681     player->effective_mouse_action.button = 0;
3682     player->effective_mouse_action.button_hint = 0;
3683
3684     for (j = 0; j < MAX_NUM_KEYS; j++)
3685       player->key[j] = FALSE;
3686
3687     player->num_white_keys = 0;
3688
3689     player->dynabomb_count = 0;
3690     player->dynabomb_size = 1;
3691     player->dynabombs_left = 0;
3692     player->dynabomb_xl = FALSE;
3693
3694     player->MovDir = initial_move_dir;
3695     player->MovPos = 0;
3696     player->GfxPos = 0;
3697     player->GfxDir = initial_move_dir;
3698     player->GfxAction = ACTION_DEFAULT;
3699     player->Frame = 0;
3700     player->StepFrame = 0;
3701
3702     player->initial_element = player->element_nr;
3703     player->artwork_element =
3704       (level.use_artwork_element[i] ? level.artwork_element[i] :
3705        player->element_nr);
3706     player->use_murphy = FALSE;
3707
3708     player->block_last_field = FALSE;   // initialized in InitPlayerField()
3709     player->block_delay_adjustment = 0; // initialized in InitPlayerField()
3710
3711     player->gravity = level.initial_player_gravity[i];
3712
3713     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3714
3715     player->actual_frame_counter.count = 0;
3716     player->actual_frame_counter.value = 1;
3717
3718     player->step_counter = 0;
3719
3720     player->last_move_dir = initial_move_dir;
3721
3722     player->is_active = FALSE;
3723
3724     player->is_waiting = FALSE;
3725     player->is_moving = FALSE;
3726     player->is_auto_moving = FALSE;
3727     player->is_digging = FALSE;
3728     player->is_snapping = FALSE;
3729     player->is_collecting = FALSE;
3730     player->is_pushing = FALSE;
3731     player->is_switching = FALSE;
3732     player->is_dropping = FALSE;
3733     player->is_dropping_pressed = FALSE;
3734
3735     player->is_bored = FALSE;
3736     player->is_sleeping = FALSE;
3737
3738     player->was_waiting = TRUE;
3739     player->was_moving = FALSE;
3740     player->was_snapping = FALSE;
3741     player->was_dropping = FALSE;
3742
3743     player->force_dropping = FALSE;
3744
3745     player->frame_counter_bored = -1;
3746     player->frame_counter_sleeping = -1;
3747
3748     player->anim_delay_counter = 0;
3749     player->post_delay_counter = 0;
3750
3751     player->dir_waiting = initial_move_dir;
3752     player->action_waiting = ACTION_DEFAULT;
3753     player->last_action_waiting = ACTION_DEFAULT;
3754     player->special_action_bored = ACTION_DEFAULT;
3755     player->special_action_sleeping = ACTION_DEFAULT;
3756
3757     player->switch_x = -1;
3758     player->switch_y = -1;
3759
3760     player->drop_x = -1;
3761     player->drop_y = -1;
3762
3763     player->show_envelope = 0;
3764
3765     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3766
3767     player->push_delay       = -1;      // initialized when pushing starts
3768     player->push_delay_value = game.initial_push_delay_value;
3769
3770     player->drop_delay = 0;
3771     player->drop_pressed_delay = 0;
3772
3773     player->last_jx = -1;
3774     player->last_jy = -1;
3775     player->jx = -1;
3776     player->jy = -1;
3777
3778     player->shield_normal_time_left = 0;
3779     player->shield_deadly_time_left = 0;
3780
3781     player->last_removed_element = EL_UNDEFINED;
3782
3783     player->inventory_infinite_element = EL_UNDEFINED;
3784     player->inventory_size = 0;
3785
3786     if (level.use_initial_inventory[i])
3787     {
3788       for (j = 0; j < level.initial_inventory_size[i]; j++)
3789       {
3790         int element = level.initial_inventory_content[i][j];
3791         int collect_count = element_info[element].collect_count_initial;
3792         int k;
3793
3794         if (!IS_CUSTOM_ELEMENT(element))
3795           collect_count = 1;
3796
3797         if (collect_count == 0)
3798           player->inventory_infinite_element = element;
3799         else
3800           for (k = 0; k < collect_count; k++)
3801             if (player->inventory_size < MAX_INVENTORY_SIZE)
3802               player->inventory_element[player->inventory_size++] = element;
3803       }
3804     }
3805
3806     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3807     SnapField(player, 0, 0);
3808
3809     map_player_action[i] = i;
3810   }
3811
3812   network_player_action_received = FALSE;
3813
3814   // initial null action
3815   if (network_playing)
3816     SendToServer_MovePlayer(MV_NONE);
3817
3818   FrameCounter = 0;
3819   TimeFrames = 0;
3820   TimePlayed = 0;
3821   TimeLeft = level.time;
3822   TapeTime = 0;
3823
3824   ScreenMovDir = MV_NONE;
3825   ScreenMovPos = 0;
3826   ScreenGfxPos = 0;
3827
3828   ScrollStepSize = 0;   // will be correctly initialized by ScrollScreen()
3829
3830   game.robot_wheel_x = -1;
3831   game.robot_wheel_y = -1;
3832
3833   game.exit_x = -1;
3834   game.exit_y = -1;
3835
3836   game.all_players_gone = FALSE;
3837
3838   game.LevelSolved = FALSE;
3839   game.GameOver = FALSE;
3840
3841   game.GamePlayed = !tape.playing;
3842
3843   game.LevelSolved_GameWon = FALSE;
3844   game.LevelSolved_GameEnd = FALSE;
3845   game.LevelSolved_SaveTape = FALSE;
3846   game.LevelSolved_SaveScore = FALSE;
3847
3848   game.LevelSolved_CountingTime = 0;
3849   game.LevelSolved_CountingScore = 0;
3850   game.LevelSolved_CountingHealth = 0;
3851
3852   game.panel.active = TRUE;
3853
3854   game.no_level_time_limit = (level.time == 0);
3855   game.time_limit = (leveldir_current->time_limit && setup.time_limit);
3856
3857   game.yamyam_content_nr = 0;
3858   game.robot_wheel_active = FALSE;
3859   game.magic_wall_active = FALSE;
3860   game.magic_wall_time_left = 0;
3861   game.light_time_left = 0;
3862   game.timegate_time_left = 0;
3863   game.switchgate_pos = 0;
3864   game.wind_direction = level.wind_direction_initial;
3865
3866   game.time_final = 0;
3867   game.score_time_final = 0;
3868
3869   game.score = 0;
3870   game.score_final = 0;
3871
3872   game.health = MAX_HEALTH;
3873   game.health_final = MAX_HEALTH;
3874
3875   game.gems_still_needed = level.gems_needed;
3876   game.sokoban_fields_still_needed = 0;
3877   game.sokoban_objects_still_needed = 0;
3878   game.lights_still_needed = 0;
3879   game.players_still_needed = 0;
3880   game.friends_still_needed = 0;
3881
3882   game.lenses_time_left = 0;
3883   game.magnify_time_left = 0;
3884
3885   game.ball_active = level.ball_active_initial;
3886   game.ball_content_nr = 0;
3887
3888   game.explosions_delayed = TRUE;
3889
3890   game.envelope_active = FALSE;
3891
3892   // special case: set custom artwork setting to initial value
3893   game.use_masked_elements = game.use_masked_elements_initial;
3894
3895   for (i = 0; i < NUM_BELTS; i++)
3896   {
3897     game.belt_dir[i] = MV_NONE;
3898     game.belt_dir_nr[i] = 3;            // not moving, next moving left
3899   }
3900
3901   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3902     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3903
3904 #if DEBUG_INIT_PLAYER
3905   DebugPrintPlayerStatus("Player status at level initialization");
3906 #endif
3907
3908   SCAN_PLAYFIELD(x, y)
3909   {
3910     Tile[x][y] = Last[x][y] = level.field[x][y];
3911     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3912     ChangeDelay[x][y] = 0;
3913     ChangePage[x][y] = -1;
3914     CustomValue[x][y] = 0;              // initialized in InitField()
3915     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3916     AmoebaNr[x][y] = 0;
3917     WasJustMoving[x][y] = 0;
3918     WasJustFalling[x][y] = 0;
3919     CheckCollision[x][y] = 0;
3920     CheckImpact[x][y] = 0;
3921     Stop[x][y] = FALSE;
3922     Pushed[x][y] = FALSE;
3923
3924     ChangeCount[x][y] = 0;
3925     ChangeEvent[x][y] = -1;
3926
3927     ExplodePhase[x][y] = 0;
3928     ExplodeDelay[x][y] = 0;
3929     ExplodeField[x][y] = EX_TYPE_NONE;
3930
3931     RunnerVisit[x][y] = 0;
3932     PlayerVisit[x][y] = 0;
3933
3934     GfxFrame[x][y] = 0;
3935     GfxRandom[x][y] = INIT_GFX_RANDOM();
3936     GfxRandomStatic[x][y] = INIT_GFX_RANDOM();
3937     GfxElement[x][y] = EL_UNDEFINED;
3938     GfxElementEmpty[x][y] = EL_EMPTY;
3939     GfxAction[x][y] = ACTION_DEFAULT;
3940     GfxDir[x][y] = MV_NONE;
3941     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3942   }
3943
3944   SCAN_PLAYFIELD(x, y)
3945   {
3946     if (emulate_bd && !IS_BD_ELEMENT(Tile[x][y]))
3947       emulate_bd = FALSE;
3948     if (emulate_sp && !IS_SP_ELEMENT(Tile[x][y]))
3949       emulate_sp = FALSE;
3950
3951     InitField(x, y, TRUE);
3952
3953     ResetGfxAnimation(x, y);
3954   }
3955
3956   InitBeltMovement();
3957
3958   for (i = 0; i < MAX_PLAYERS; i++)
3959   {
3960     struct PlayerInfo *player = &stored_player[i];
3961
3962     // set number of special actions for bored and sleeping animation
3963     player->num_special_action_bored =
3964       get_num_special_action(player->artwork_element,
3965                              ACTION_BORING_1, ACTION_BORING_LAST);
3966     player->num_special_action_sleeping =
3967       get_num_special_action(player->artwork_element,
3968                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3969   }
3970
3971   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3972                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3973
3974   // initialize type of slippery elements
3975   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3976   {
3977     if (!IS_CUSTOM_ELEMENT(i))
3978     {
3979       // default: elements slip down either to the left or right randomly
3980       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3981
3982       // SP style elements prefer to slip down on the left side
3983       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3984         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3985
3986       // BD style elements prefer to slip down on the left side
3987       if (game.emulation == EMU_BOULDERDASH)
3988         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3989     }
3990   }
3991
3992   // initialize explosion and ignition delay
3993   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3994   {
3995     if (!IS_CUSTOM_ELEMENT(i))
3996     {
3997       int num_phase = 8;
3998       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3999                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
4000                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
4001       int last_phase = (num_phase + 1) * delay;
4002       int half_phase = (num_phase / 2) * delay;
4003
4004       element_info[i].explosion_delay = last_phase - 1;
4005       element_info[i].ignition_delay = half_phase;
4006
4007       if (i == EL_BLACK_ORB)
4008         element_info[i].ignition_delay = 1;
4009     }
4010   }
4011
4012   // correct non-moving belts to start moving left
4013   for (i = 0; i < NUM_BELTS; i++)
4014     if (game.belt_dir[i] == MV_NONE)
4015       game.belt_dir_nr[i] = 3;          // not moving, next moving left
4016
4017 #if USE_NEW_PLAYER_ASSIGNMENTS
4018   // use preferred player also in local single-player mode
4019   if (!network.enabled && !game.team_mode)
4020   {
4021     int new_index_nr = setup.network_player_nr;
4022
4023     if (new_index_nr >= 0 && new_index_nr < MAX_PLAYERS)
4024     {
4025       for (i = 0; i < MAX_PLAYERS; i++)
4026         stored_player[i].connected_locally = FALSE;
4027
4028       stored_player[new_index_nr].connected_locally = TRUE;
4029     }
4030   }
4031
4032   for (i = 0; i < MAX_PLAYERS; i++)
4033   {
4034     stored_player[i].connected = FALSE;
4035
4036     // in network game mode, the local player might not be the first player
4037     if (stored_player[i].connected_locally)
4038       local_player = &stored_player[i];
4039   }
4040
4041   if (!network.enabled)
4042     local_player->connected = TRUE;
4043
4044   if (tape.playing)
4045   {
4046     for (i = 0; i < MAX_PLAYERS; i++)
4047       stored_player[i].connected = tape.player_participates[i];
4048   }
4049   else if (network.enabled)
4050   {
4051     // add team mode players connected over the network (needed for correct
4052     // assignment of player figures from level to locally playing players)
4053
4054     for (i = 0; i < MAX_PLAYERS; i++)
4055       if (stored_player[i].connected_network)
4056         stored_player[i].connected = TRUE;
4057   }
4058   else if (game.team_mode)
4059   {
4060     // try to guess locally connected team mode players (needed for correct
4061     // assignment of player figures from level to locally playing players)
4062
4063     for (i = 0; i < MAX_PLAYERS; i++)
4064       if (setup.input[i].use_joystick ||
4065           setup.input[i].key.left != KSYM_UNDEFINED)
4066         stored_player[i].connected = TRUE;
4067   }
4068
4069 #if DEBUG_INIT_PLAYER
4070   DebugPrintPlayerStatus("Player status after level initialization");
4071 #endif
4072
4073 #if DEBUG_INIT_PLAYER
4074   Debug("game:init:player", "Reassigning players ...");
4075 #endif
4076
4077   // check if any connected player was not found in playfield
4078   for (i = 0; i < MAX_PLAYERS; i++)
4079   {
4080     struct PlayerInfo *player = &stored_player[i];
4081
4082     if (player->connected && !player->present)
4083     {
4084       struct PlayerInfo *field_player = NULL;
4085
4086 #if DEBUG_INIT_PLAYER
4087       Debug("game:init:player",
4088             "- looking for field player for player %d ...", i + 1);
4089 #endif
4090
4091       // assign first free player found that is present in the playfield
4092
4093       // first try: look for unmapped playfield player that is not connected
4094       for (j = 0; j < MAX_PLAYERS; j++)
4095         if (field_player == NULL &&
4096             stored_player[j].present &&
4097             !stored_player[j].mapped &&
4098             !stored_player[j].connected)
4099           field_player = &stored_player[j];
4100
4101       // second try: look for *any* unmapped playfield player
4102       for (j = 0; j < MAX_PLAYERS; j++)
4103         if (field_player == NULL &&
4104             stored_player[j].present &&
4105             !stored_player[j].mapped)
4106           field_player = &stored_player[j];
4107
4108       if (field_player != NULL)
4109       {
4110         int jx = field_player->jx, jy = field_player->jy;
4111
4112 #if DEBUG_INIT_PLAYER
4113         Debug("game:init:player", "- found player %d",
4114               field_player->index_nr + 1);
4115 #endif
4116
4117         player->present = FALSE;
4118         player->active = FALSE;
4119
4120         field_player->present = TRUE;
4121         field_player->active = TRUE;
4122
4123         /*
4124         player->initial_element = field_player->initial_element;
4125         player->artwork_element = field_player->artwork_element;
4126
4127         player->block_last_field       = field_player->block_last_field;
4128         player->block_delay_adjustment = field_player->block_delay_adjustment;
4129         */
4130
4131         StorePlayer[jx][jy] = field_player->element_nr;
4132
4133         field_player->jx = field_player->last_jx = jx;
4134         field_player->jy = field_player->last_jy = jy;
4135
4136         if (local_player == player)
4137           local_player = field_player;
4138
4139         map_player_action[field_player->index_nr] = i;
4140
4141         field_player->mapped = TRUE;
4142
4143 #if DEBUG_INIT_PLAYER
4144         Debug("game:init:player", "- map_player_action[%d] == %d",
4145               field_player->index_nr + 1, i + 1);
4146 #endif
4147       }
4148     }
4149
4150     if (player->connected && player->present)
4151       player->mapped = TRUE;
4152   }
4153
4154 #if DEBUG_INIT_PLAYER
4155   DebugPrintPlayerStatus("Player status after player assignment (first stage)");
4156 #endif
4157
4158 #else
4159
4160   // check if any connected player was not found in playfield
4161   for (i = 0; i < MAX_PLAYERS; i++)
4162   {
4163     struct PlayerInfo *player = &stored_player[i];
4164
4165     if (player->connected && !player->present)
4166     {
4167       for (j = 0; j < MAX_PLAYERS; j++)
4168       {
4169         struct PlayerInfo *field_player = &stored_player[j];
4170         int jx = field_player->jx, jy = field_player->jy;
4171
4172         // assign first free player found that is present in the playfield
4173         if (field_player->present && !field_player->connected)
4174         {
4175           player->present = TRUE;
4176           player->active = TRUE;
4177
4178           field_player->present = FALSE;
4179           field_player->active = FALSE;
4180
4181           player->initial_element = field_player->initial_element;
4182           player->artwork_element = field_player->artwork_element;
4183
4184           player->block_last_field       = field_player->block_last_field;
4185           player->block_delay_adjustment = field_player->block_delay_adjustment;
4186
4187           StorePlayer[jx][jy] = player->element_nr;
4188
4189           player->jx = player->last_jx = jx;
4190           player->jy = player->last_jy = jy;
4191
4192           break;
4193         }
4194       }
4195     }
4196   }
4197 #endif
4198
4199 #if 0
4200   Debug("game:init:player", "local_player->present == %d",
4201         local_player->present);
4202 #endif
4203
4204   // set focus to local player for network games, else to all players
4205   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
4206   game.centered_player_nr_next = game.centered_player_nr;
4207   game.set_centered_player = FALSE;
4208   game.set_centered_player_wrap = FALSE;
4209
4210   if (network_playing && tape.recording)
4211   {
4212     // store client dependent player focus when recording network games
4213     tape.centered_player_nr_next = game.centered_player_nr_next;
4214     tape.set_centered_player = TRUE;
4215   }
4216
4217   if (tape.playing)
4218   {
4219     // when playing a tape, eliminate all players who do not participate
4220
4221 #if USE_NEW_PLAYER_ASSIGNMENTS
4222
4223     if (!game.team_mode)
4224     {
4225       for (i = 0; i < MAX_PLAYERS; i++)
4226       {
4227         if (stored_player[i].active &&
4228             !tape.player_participates[map_player_action[i]])
4229         {
4230           struct PlayerInfo *player = &stored_player[i];
4231           int jx = player->jx, jy = player->jy;
4232
4233 #if DEBUG_INIT_PLAYER
4234           Debug("game:init:player", "Removing player %d at (%d, %d)",
4235                 i + 1, jx, jy);
4236 #endif
4237
4238           player->active = FALSE;
4239           StorePlayer[jx][jy] = 0;
4240           Tile[jx][jy] = EL_EMPTY;
4241         }
4242       }
4243     }
4244
4245 #else
4246
4247     for (i = 0; i < MAX_PLAYERS; i++)
4248     {
4249       if (stored_player[i].active &&
4250           !tape.player_participates[i])
4251       {
4252         struct PlayerInfo *player = &stored_player[i];
4253         int jx = player->jx, jy = player->jy;
4254
4255         player->active = FALSE;
4256         StorePlayer[jx][jy] = 0;
4257         Tile[jx][jy] = EL_EMPTY;
4258       }
4259     }
4260 #endif
4261   }
4262   else if (!network.enabled && !game.team_mode)         // && !tape.playing
4263   {
4264     // when in single player mode, eliminate all but the local player
4265
4266     for (i = 0; i < MAX_PLAYERS; i++)
4267     {
4268       struct PlayerInfo *player = &stored_player[i];
4269
4270       if (player->active && player != local_player)
4271       {
4272         int jx = player->jx, jy = player->jy;
4273
4274         player->active = FALSE;
4275         player->present = FALSE;
4276
4277         StorePlayer[jx][jy] = 0;
4278         Tile[jx][jy] = EL_EMPTY;
4279       }
4280     }
4281   }
4282
4283   for (i = 0; i < MAX_PLAYERS; i++)
4284     if (stored_player[i].active)
4285       game.players_still_needed++;
4286
4287   if (level.solved_by_one_player)
4288     game.players_still_needed = 1;
4289
4290   // when recording the game, store which players take part in the game
4291   if (tape.recording)
4292   {
4293 #if USE_NEW_PLAYER_ASSIGNMENTS
4294     for (i = 0; i < MAX_PLAYERS; i++)
4295       if (stored_player[i].connected)
4296         tape.player_participates[i] = TRUE;
4297 #else
4298     for (i = 0; i < MAX_PLAYERS; i++)
4299       if (stored_player[i].active)
4300         tape.player_participates[i] = TRUE;
4301 #endif
4302   }
4303
4304 #if DEBUG_INIT_PLAYER
4305   DebugPrintPlayerStatus("Player status after player assignment (final stage)");
4306 #endif
4307
4308   if (BorderElement == EL_EMPTY)
4309   {
4310     SBX_Left = 0;
4311     SBX_Right = lev_fieldx - SCR_FIELDX;
4312     SBY_Upper = 0;
4313     SBY_Lower = lev_fieldy - SCR_FIELDY;
4314   }
4315   else
4316   {
4317     SBX_Left = -1;
4318     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4319     SBY_Upper = -1;
4320     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4321   }
4322
4323   if (full_lev_fieldx <= SCR_FIELDX)
4324     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4325   if (full_lev_fieldy <= SCR_FIELDY)
4326     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4327
4328   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4329     SBX_Left--;
4330   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4331     SBY_Upper--;
4332
4333   // if local player not found, look for custom element that might create
4334   // the player (make some assumptions about the right custom element)
4335   if (!local_player->present)
4336   {
4337     int start_x = 0, start_y = 0;
4338     int found_rating = 0;
4339     int found_element = EL_UNDEFINED;
4340     int player_nr = local_player->index_nr;
4341
4342     SCAN_PLAYFIELD(x, y)
4343     {
4344       int element = Tile[x][y];
4345       int content;
4346       int xx, yy;
4347       boolean is_player;
4348
4349       if (level.use_start_element[player_nr] &&
4350           level.start_element[player_nr] == element &&
4351           found_rating < 4)
4352       {
4353         start_x = x;
4354         start_y = y;
4355
4356         found_rating = 4;
4357         found_element = element;
4358       }
4359
4360       if (!IS_CUSTOM_ELEMENT(element))
4361         continue;
4362
4363       if (CAN_CHANGE(element))
4364       {
4365         for (i = 0; i < element_info[element].num_change_pages; i++)
4366         {
4367           // check for player created from custom element as single target
4368           content = element_info[element].change_page[i].target_element;
4369           is_player = IS_PLAYER_ELEMENT(content);
4370
4371           if (is_player && (found_rating < 3 ||
4372                             (found_rating == 3 && element < found_element)))
4373           {
4374             start_x = x;
4375             start_y = y;
4376
4377             found_rating = 3;
4378             found_element = element;
4379           }
4380         }
4381       }
4382
4383       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4384       {
4385         // check for player created from custom element as explosion content
4386         content = element_info[element].content.e[xx][yy];
4387         is_player = IS_PLAYER_ELEMENT(content);
4388
4389         if (is_player && (found_rating < 2 ||
4390                           (found_rating == 2 && element < found_element)))
4391         {
4392           start_x = x + xx - 1;
4393           start_y = y + yy - 1;
4394
4395           found_rating = 2;
4396           found_element = element;
4397         }
4398
4399         if (!CAN_CHANGE(element))
4400           continue;
4401
4402         for (i = 0; i < element_info[element].num_change_pages; i++)
4403         {
4404           // check for player created from custom element as extended target
4405           content =
4406             element_info[element].change_page[i].target_content.e[xx][yy];
4407
4408           is_player = IS_PLAYER_ELEMENT(content);
4409
4410           if (is_player && (found_rating < 1 ||
4411                             (found_rating == 1 && element < found_element)))
4412           {
4413             start_x = x + xx - 1;
4414             start_y = y + yy - 1;
4415
4416             found_rating = 1;
4417             found_element = element;
4418           }
4419         }
4420       }
4421     }
4422
4423     scroll_x = SCROLL_POSITION_X(start_x);
4424     scroll_y = SCROLL_POSITION_Y(start_y);
4425   }
4426   else
4427   {
4428     scroll_x = SCROLL_POSITION_X(local_player->jx);
4429     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4430   }
4431
4432   // !!! FIX THIS (START) !!!
4433   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4434   {
4435     InitGameEngine_EM();
4436   }
4437   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4438   {
4439     InitGameEngine_SP();
4440   }
4441   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4442   {
4443     InitGameEngine_MM();
4444   }
4445   else
4446   {
4447     DrawLevel(REDRAW_FIELD);
4448     DrawAllPlayers();
4449
4450     // after drawing the level, correct some elements
4451     if (game.timegate_time_left == 0)
4452       CloseAllOpenTimegates();
4453   }
4454
4455   // blit playfield from scroll buffer to normal back buffer for fading in
4456   BlitScreenToBitmap(backbuffer);
4457   // !!! FIX THIS (END) !!!
4458
4459   DrawMaskedBorder(fade_mask);
4460
4461   FadeIn(fade_mask);
4462
4463 #if 1
4464   // full screen redraw is required at this point in the following cases:
4465   // - special editor door undrawn when game was started from level editor
4466   // - drawing area (playfield) was changed and has to be removed completely
4467   redraw_mask = REDRAW_ALL;
4468   BackToFront();
4469 #endif
4470
4471   if (!game.restart_level)
4472   {
4473     // copy default game door content to main double buffer
4474
4475     // !!! CHECK AGAIN !!!
4476     SetPanelBackground();
4477     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4478     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4479   }
4480
4481   SetPanelBackground();
4482   SetDrawBackgroundMask(REDRAW_DOOR_1);
4483
4484   UpdateAndDisplayGameControlValues();
4485
4486   if (!game.restart_level)
4487   {
4488     UnmapGameButtons();
4489     UnmapTapeButtons();
4490
4491     FreeGameButtons();
4492     CreateGameButtons();
4493
4494     MapGameButtons();
4495     MapTapeButtons();
4496
4497     // copy actual game door content to door double buffer for OpenDoor()
4498     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4499
4500     OpenDoor(DOOR_OPEN_ALL);
4501
4502     KeyboardAutoRepeatOffUnlessAutoplay();
4503
4504 #if DEBUG_INIT_PLAYER
4505     DebugPrintPlayerStatus("Player status (final)");
4506 #endif
4507   }
4508
4509   UnmapAllGadgets();
4510
4511   MapGameButtons();
4512   MapTapeButtons();
4513
4514   if (!game.restart_level && !tape.playing)
4515   {
4516     LevelStats_incPlayed(level_nr);
4517
4518     SaveLevelSetup_SeriesInfo();
4519   }
4520
4521   game.restart_level = FALSE;
4522
4523   game.request_active = FALSE;
4524   game.request_active_or_moving = FALSE;
4525
4526   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4527     InitGameActions_MM();
4528
4529   SaveEngineSnapshotToListInitial();
4530
4531   if (!game.restart_level)
4532   {
4533     PlaySound(SND_GAME_STARTING);
4534
4535     if (setup.sound_music)
4536       PlayLevelMusic();
4537   }
4538
4539   SetPlayfieldMouseCursorEnabled(!game.use_mouse_actions);
4540 }
4541
4542 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4543                         int actual_player_x, int actual_player_y)
4544 {
4545   // this is used for non-R'n'D game engines to update certain engine values
4546
4547   // needed to determine if sounds are played within the visible screen area
4548   scroll_x = actual_scroll_x;
4549   scroll_y = actual_scroll_y;
4550
4551   // needed to get player position for "follow finger" playing input method
4552   local_player->jx = actual_player_x;
4553   local_player->jy = actual_player_y;
4554 }
4555
4556 void InitMovDir(int x, int y)
4557 {
4558   int i, element = Tile[x][y];
4559   static int xy[4][2] =
4560   {
4561     {  0, +1 },
4562     { +1,  0 },
4563     {  0, -1 },
4564     { -1,  0 }
4565   };
4566   static int direction[3][4] =
4567   {
4568     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4569     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4570     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4571   };
4572
4573   switch (element)
4574   {
4575     case EL_BUG_RIGHT:
4576     case EL_BUG_UP:
4577     case EL_BUG_LEFT:
4578     case EL_BUG_DOWN:
4579       Tile[x][y] = EL_BUG;
4580       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4581       break;
4582
4583     case EL_SPACESHIP_RIGHT:
4584     case EL_SPACESHIP_UP:
4585     case EL_SPACESHIP_LEFT:
4586     case EL_SPACESHIP_DOWN:
4587       Tile[x][y] = EL_SPACESHIP;
4588       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4589       break;
4590
4591     case EL_BD_BUTTERFLY_RIGHT:
4592     case EL_BD_BUTTERFLY_UP:
4593     case EL_BD_BUTTERFLY_LEFT:
4594     case EL_BD_BUTTERFLY_DOWN:
4595       Tile[x][y] = EL_BD_BUTTERFLY;
4596       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4597       break;
4598
4599     case EL_BD_FIREFLY_RIGHT:
4600     case EL_BD_FIREFLY_UP:
4601     case EL_BD_FIREFLY_LEFT:
4602     case EL_BD_FIREFLY_DOWN:
4603       Tile[x][y] = EL_BD_FIREFLY;
4604       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4605       break;
4606
4607     case EL_PACMAN_RIGHT:
4608     case EL_PACMAN_UP:
4609     case EL_PACMAN_LEFT:
4610     case EL_PACMAN_DOWN:
4611       Tile[x][y] = EL_PACMAN;
4612       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4613       break;
4614
4615     case EL_YAMYAM_LEFT:
4616     case EL_YAMYAM_RIGHT:
4617     case EL_YAMYAM_UP:
4618     case EL_YAMYAM_DOWN:
4619       Tile[x][y] = EL_YAMYAM;
4620       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4621       break;
4622
4623     case EL_SP_SNIKSNAK:
4624       MovDir[x][y] = MV_UP;
4625       break;
4626
4627     case EL_SP_ELECTRON:
4628       MovDir[x][y] = MV_LEFT;
4629       break;
4630
4631     case EL_MOLE_LEFT:
4632     case EL_MOLE_RIGHT:
4633     case EL_MOLE_UP:
4634     case EL_MOLE_DOWN:
4635       Tile[x][y] = EL_MOLE;
4636       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4637       break;
4638
4639     case EL_SPRING_LEFT:
4640     case EL_SPRING_RIGHT:
4641       Tile[x][y] = EL_SPRING;
4642       MovDir[x][y] = direction[2][element - EL_SPRING_LEFT];
4643       break;
4644
4645     default:
4646       if (IS_CUSTOM_ELEMENT(element))
4647       {
4648         struct ElementInfo *ei = &element_info[element];
4649         int move_direction_initial = ei->move_direction_initial;
4650         int move_pattern = ei->move_pattern;
4651
4652         if (move_direction_initial == MV_START_PREVIOUS)
4653         {
4654           if (MovDir[x][y] != MV_NONE)
4655             return;
4656
4657           move_direction_initial = MV_START_AUTOMATIC;
4658         }
4659
4660         if (move_direction_initial == MV_START_RANDOM)
4661           MovDir[x][y] = 1 << RND(4);
4662         else if (move_direction_initial & MV_ANY_DIRECTION)
4663           MovDir[x][y] = move_direction_initial;
4664         else if (move_pattern == MV_ALL_DIRECTIONS ||
4665                  move_pattern == MV_TURNING_LEFT ||
4666                  move_pattern == MV_TURNING_RIGHT ||
4667                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4668                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4669                  move_pattern == MV_TURNING_RANDOM)
4670           MovDir[x][y] = 1 << RND(4);
4671         else if (move_pattern == MV_HORIZONTAL)
4672           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4673         else if (move_pattern == MV_VERTICAL)
4674           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4675         else if (move_pattern & MV_ANY_DIRECTION)
4676           MovDir[x][y] = element_info[element].move_pattern;
4677         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4678                  move_pattern == MV_ALONG_RIGHT_SIDE)
4679         {
4680           // use random direction as default start direction
4681           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4682             MovDir[x][y] = 1 << RND(4);
4683
4684           for (i = 0; i < NUM_DIRECTIONS; i++)
4685           {
4686             int x1 = x + xy[i][0];
4687             int y1 = y + xy[i][1];
4688
4689             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4690             {
4691               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4692                 MovDir[x][y] = direction[0][i];
4693               else
4694                 MovDir[x][y] = direction[1][i];
4695
4696               break;
4697             }
4698           }
4699         }                
4700       }
4701       else
4702       {
4703         MovDir[x][y] = 1 << RND(4);
4704
4705         if (element != EL_BUG &&
4706             element != EL_SPACESHIP &&
4707             element != EL_BD_BUTTERFLY &&
4708             element != EL_BD_FIREFLY)
4709           break;
4710
4711         for (i = 0; i < NUM_DIRECTIONS; i++)
4712         {
4713           int x1 = x + xy[i][0];
4714           int y1 = y + xy[i][1];
4715
4716           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4717           {
4718             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4719             {
4720               MovDir[x][y] = direction[0][i];
4721               break;
4722             }
4723             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4724                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4725             {
4726               MovDir[x][y] = direction[1][i];
4727               break;
4728             }
4729           }
4730         }
4731       }
4732       break;
4733   }
4734
4735   GfxDir[x][y] = MovDir[x][y];
4736 }
4737
4738 void InitAmoebaNr(int x, int y)
4739 {
4740   int i;
4741   int group_nr = AmoebaNeighbourNr(x, y);
4742
4743   if (group_nr == 0)
4744   {
4745     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4746     {
4747       if (AmoebaCnt[i] == 0)
4748       {
4749         group_nr = i;
4750         break;
4751       }
4752     }
4753   }
4754
4755   AmoebaNr[x][y] = group_nr;
4756   AmoebaCnt[group_nr]++;
4757   AmoebaCnt2[group_nr]++;
4758 }
4759
4760 static void LevelSolved_SetFinalGameValues(void)
4761 {
4762   game.time_final = (game.no_level_time_limit ? TimePlayed : TimeLeft);
4763   game.score_time_final = (level.use_step_counter ? TimePlayed :
4764                            TimePlayed * FRAMES_PER_SECOND + TimeFrames);
4765
4766   game.score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4767                       game_em.lev->score :
4768                       level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4769                       game_mm.score :
4770                       game.score);
4771
4772   game.health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4773                        MM_HEALTH(game_mm.laser_overload_value) :
4774                        game.health);
4775
4776   game.LevelSolved_CountingTime = game.time_final;
4777   game.LevelSolved_CountingScore = game.score_final;
4778   game.LevelSolved_CountingHealth = game.health_final;
4779 }
4780
4781 static void LevelSolved_DisplayFinalGameValues(int time, int score, int health)
4782 {
4783   game.LevelSolved_CountingTime = time;
4784   game.LevelSolved_CountingScore = score;
4785   game.LevelSolved_CountingHealth = health;
4786
4787   game_panel_controls[GAME_PANEL_TIME].value = time;
4788   game_panel_controls[GAME_PANEL_SCORE].value = score;
4789   game_panel_controls[GAME_PANEL_HEALTH].value = health;
4790
4791   DisplayGameControlValues();
4792 }
4793
4794 static void LevelSolved(void)
4795 {
4796   if (level.game_engine_type == GAME_ENGINE_TYPE_RND &&
4797       game.players_still_needed > 0)
4798     return;
4799
4800   game.LevelSolved = TRUE;
4801   game.GameOver = TRUE;
4802
4803   tape.solved = TRUE;
4804
4805   // needed here to display correct panel values while player walks into exit
4806   LevelSolved_SetFinalGameValues();
4807 }
4808
4809 void GameWon(void)
4810 {
4811   static int time_count_steps;
4812   static int time, time_final;
4813   static float score, score_final; // needed for time score < 10 for 10 seconds
4814   static int health, health_final;
4815   static int game_over_delay_1 = 0;
4816   static int game_over_delay_2 = 0;
4817   static int game_over_delay_3 = 0;
4818   int time_score_base = MIN(MAX(1, level.time_score_base), 10);
4819   float time_score = (float)level.score[SC_TIME_BONUS] / time_score_base;
4820
4821   if (!game.LevelSolved_GameWon)
4822   {
4823     int i;
4824
4825     // do not start end game actions before the player stops moving (to exit)
4826     if (local_player->active && local_player->MovPos)
4827       return;
4828
4829     // calculate final game values after player finished walking into exit
4830     LevelSolved_SetFinalGameValues();
4831
4832     game.LevelSolved_GameWon = TRUE;
4833     game.LevelSolved_SaveTape = tape.recording;
4834     game.LevelSolved_SaveScore = !tape.playing;
4835
4836     if (!tape.playing)
4837     {
4838       LevelStats_incSolved(level_nr);
4839
4840       SaveLevelSetup_SeriesInfo();
4841     }
4842
4843     if (tape.auto_play)         // tape might already be stopped here
4844       tape.auto_play_level_solved = TRUE;
4845
4846     TapeStop();
4847
4848     game_over_delay_1 = FRAMES_PER_SECOND;      // delay before counting time
4849     game_over_delay_2 = FRAMES_PER_SECOND / 2;  // delay before counting health
4850     game_over_delay_3 = FRAMES_PER_SECOND;      // delay before ending the game
4851
4852     time = time_final = game.time_final;
4853     score = score_final = game.score_final;
4854     health = health_final = game.health_final;
4855
4856     // update game panel values before (delayed) counting of score (if any)
4857     LevelSolved_DisplayFinalGameValues(time, score, health);
4858
4859     // if level has time score defined, calculate new final game values
4860     if (time_score > 0)
4861     {
4862       int time_final_max = 999;
4863       int time_frames_final_max = time_final_max * FRAMES_PER_SECOND;
4864       int time_frames = 0;
4865       int time_frames_left = TimeLeft * FRAMES_PER_SECOND - TimeFrames;
4866       int time_frames_played = TimePlayed * FRAMES_PER_SECOND + TimeFrames;
4867
4868       if (TimeLeft > 0)
4869       {
4870         time_final = 0;
4871         time_frames = time_frames_left;
4872       }
4873       else if (game.no_level_time_limit && TimePlayed < time_final_max)
4874       {
4875         time_final = time_final_max;
4876         time_frames = time_frames_final_max - time_frames_played;
4877       }
4878
4879       score_final += time_score * time_frames / FRAMES_PER_SECOND + 0.5;
4880
4881       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4882
4883       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4884       {
4885         health_final = 0;
4886         score_final += health * time_score;
4887       }
4888
4889       game.score_final = score_final;
4890       game.health_final = health_final;
4891     }
4892
4893     // if not counting score after game, immediately update game panel values
4894     if (level_editor_test_game || !setup.count_score_after_game)
4895     {
4896       time = time_final;
4897       score = score_final;
4898
4899       LevelSolved_DisplayFinalGameValues(time, score, health);
4900     }
4901
4902     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4903     {
4904       // check if last player has left the level
4905       if (game.exit_x >= 0 &&
4906           game.exit_y >= 0)
4907       {
4908         int x = game.exit_x;
4909         int y = game.exit_y;
4910         int element = Tile[x][y];
4911
4912         // close exit door after last player
4913         if ((game.all_players_gone &&
4914              (element == EL_EXIT_OPEN ||
4915               element == EL_SP_EXIT_OPEN ||
4916               element == EL_STEEL_EXIT_OPEN)) ||
4917             element == EL_EM_EXIT_OPEN ||
4918             element == EL_EM_STEEL_EXIT_OPEN)
4919         {
4920
4921           Tile[x][y] =
4922             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4923              element == EL_EM_EXIT_OPEN         ? EL_EM_EXIT_CLOSING :
4924              element == EL_SP_EXIT_OPEN         ? EL_SP_EXIT_CLOSING:
4925              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4926              EL_EM_STEEL_EXIT_CLOSING);
4927
4928           PlayLevelSoundElementAction(x, y, element, ACTION_CLOSING);
4929         }
4930
4931         // player disappears
4932         DrawLevelField(x, y);
4933       }
4934
4935       for (i = 0; i < MAX_PLAYERS; i++)
4936       {
4937         struct PlayerInfo *player = &stored_player[i];
4938
4939         if (player->present)
4940         {
4941           RemovePlayer(player);
4942
4943           // player disappears
4944           DrawLevelField(player->jx, player->jy);
4945         }
4946       }
4947     }
4948
4949     PlaySound(SND_GAME_WINNING);
4950   }
4951
4952   if (setup.count_score_after_game)
4953   {
4954     if (time != time_final)
4955     {
4956       if (game_over_delay_1 > 0)
4957       {
4958         game_over_delay_1--;
4959
4960         return;
4961       }
4962
4963       int time_to_go = ABS(time_final - time);
4964       int time_count_dir = (time < time_final ? +1 : -1);
4965
4966       if (time_to_go < time_count_steps)
4967         time_count_steps = 1;
4968
4969       time  += time_count_steps * time_count_dir;
4970       score += time_count_steps * time_score;
4971
4972       // set final score to correct rounding differences after counting score
4973       if (time == time_final)
4974         score = score_final;
4975
4976       LevelSolved_DisplayFinalGameValues(time, score, health);
4977
4978       if (time == time_final)
4979         StopSound(SND_GAME_LEVELTIME_BONUS);
4980       else if (setup.sound_loops)
4981         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4982       else
4983         PlaySound(SND_GAME_LEVELTIME_BONUS);
4984
4985       return;
4986     }
4987
4988     if (health != health_final)
4989     {
4990       if (game_over_delay_2 > 0)
4991       {
4992         game_over_delay_2--;
4993
4994         return;
4995       }
4996
4997       int health_count_dir = (health < health_final ? +1 : -1);
4998
4999       health += health_count_dir;
5000       score  += time_score;
5001
5002       LevelSolved_DisplayFinalGameValues(time, score, health);
5003
5004       if (health == health_final)
5005         StopSound(SND_GAME_LEVELTIME_BONUS);
5006       else if (setup.sound_loops)
5007         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
5008       else
5009         PlaySound(SND_GAME_LEVELTIME_BONUS);
5010
5011       return;
5012     }
5013   }
5014
5015   game.panel.active = FALSE;
5016
5017   if (game_over_delay_3 > 0)
5018   {
5019     game_over_delay_3--;
5020
5021     return;
5022   }
5023
5024   GameEnd();
5025 }
5026
5027 void GameEnd(void)
5028 {
5029   // used instead of "level_nr" (needed for network games)
5030   int last_level_nr = levelset.level_nr;
5031   boolean tape_saved = FALSE;
5032
5033   game.LevelSolved_GameEnd = TRUE;
5034
5035   if (game.LevelSolved_SaveTape && !score_info_tape_play)
5036   {
5037     // make sure that request dialog to save tape does not open door again
5038     if (!global.use_envelope_request)
5039       CloseDoor(DOOR_CLOSE_1);
5040
5041     // ask to save tape
5042     tape_saved = SaveTapeChecked_LevelSolved(tape.level_nr);
5043
5044     // set unique basename for score tape (also saved in high score table)
5045     strcpy(tape.score_tape_basename, getScoreTapeBasename(setup.player_name));
5046   }
5047
5048   // if no tape is to be saved, close both doors simultaneously
5049   CloseDoor(DOOR_CLOSE_ALL);
5050
5051   if (level_editor_test_game || score_info_tape_play)
5052   {
5053     SetGameStatus(GAME_MODE_MAIN);
5054
5055     DrawMainMenu();
5056
5057     return;
5058   }
5059
5060   if (!game.LevelSolved_SaveScore)
5061   {
5062     SetGameStatus(GAME_MODE_MAIN);
5063
5064     DrawMainMenu();
5065
5066     return;
5067   }
5068
5069   if (level_nr == leveldir_current->handicap_level)
5070   {
5071     leveldir_current->handicap_level++;
5072
5073     SaveLevelSetup_SeriesInfo();
5074   }
5075
5076   // save score and score tape before potentially erasing tape below
5077   NewHighScore(last_level_nr, tape_saved);
5078
5079   if (setup.increment_levels &&
5080       level_nr < leveldir_current->last_level &&
5081       !network_playing)
5082   {
5083     level_nr++;         // advance to next level
5084     TapeErase();        // start with empty tape
5085
5086     if (setup.auto_play_next_level)
5087     {
5088       scores.continue_playing = TRUE;
5089       scores.next_level_nr = level_nr;
5090
5091       LoadLevel(level_nr);
5092
5093       SaveLevelSetup_SeriesInfo();
5094     }
5095   }
5096
5097   if (scores.last_added >= 0 && setup.show_scores_after_game)
5098   {
5099     SetGameStatus(GAME_MODE_SCORES);
5100
5101     DrawHallOfFame(last_level_nr);
5102   }
5103   else if (scores.continue_playing)
5104   {
5105     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
5106   }
5107   else
5108   {
5109     SetGameStatus(GAME_MODE_MAIN);
5110
5111     DrawMainMenu();
5112   }
5113 }
5114
5115 static int addScoreEntry(struct ScoreInfo *list, struct ScoreEntry *new_entry,
5116                          boolean one_score_entry_per_name)
5117 {
5118   int i;
5119
5120   if (strEqual(new_entry->name, EMPTY_PLAYER_NAME))
5121     return -1;
5122
5123   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
5124   {
5125     struct ScoreEntry *entry = &list->entry[i];
5126     boolean score_is_better = (new_entry->score >  entry->score);
5127     boolean score_is_equal  = (new_entry->score == entry->score);
5128     boolean time_is_better  = (new_entry->time  <  entry->time);
5129     boolean time_is_equal   = (new_entry->time  == entry->time);
5130     boolean better_by_score = (score_is_better ||
5131                                (score_is_equal && time_is_better));
5132     boolean better_by_time  = (time_is_better ||
5133                                (time_is_equal && score_is_better));
5134     boolean is_better = (level.rate_time_over_score ? better_by_time :
5135                          better_by_score);
5136     boolean entry_is_empty = (entry->score == 0 &&
5137                               entry->time == 0);
5138
5139     // prevent adding server score entries if also existing in local score file
5140     // (special case: historic score entries have an empty tape basename entry)
5141     if (strEqual(new_entry->tape_basename, entry->tape_basename) &&
5142         !strEqual(new_entry->tape_basename, UNDEFINED_FILENAME))
5143     {
5144       // add fields from server score entry not stored in local score entry
5145       // (currently, this means setting platform, version and country fields;
5146       // in rare cases, this may also correct an invalid score value, as
5147       // historic scores might have been truncated to 16-bit values locally)
5148       *entry = *new_entry;
5149
5150       return -1;
5151     }
5152
5153     if (is_better || entry_is_empty)
5154     {
5155       // player has made it to the hall of fame
5156
5157       if (i < MAX_SCORE_ENTRIES - 1)
5158       {
5159         int m = MAX_SCORE_ENTRIES - 1;
5160         int l;
5161
5162         if (one_score_entry_per_name)
5163         {
5164           for (l = i; l < MAX_SCORE_ENTRIES; l++)
5165             if (strEqual(list->entry[l].name, new_entry->name))
5166               m = l;
5167
5168           if (m == i)   // player's new highscore overwrites his old one
5169             goto put_into_list;
5170         }
5171
5172         for (l = m; l > i; l--)
5173           list->entry[l] = list->entry[l - 1];
5174       }
5175
5176       put_into_list:
5177
5178       *entry = *new_entry;
5179
5180       return i;
5181     }
5182     else if (one_score_entry_per_name &&
5183              strEqual(entry->name, new_entry->name))
5184     {
5185       // player already in high score list with better score or time
5186
5187       return -1;
5188     }
5189   }
5190
5191   // special case: new score is beyond the last high score list position
5192   return MAX_SCORE_ENTRIES;
5193 }
5194
5195 void NewHighScore(int level_nr, boolean tape_saved)
5196 {
5197   struct ScoreEntry new_entry = {{ 0 }}; // (prevent warning from GCC bug 53119)
5198   boolean one_per_name = FALSE;
5199
5200   strncpy(new_entry.tape_basename, tape.score_tape_basename, MAX_FILENAME_LEN);
5201   strncpy(new_entry.name, setup.player_name, MAX_PLAYER_NAME_LEN);
5202
5203   new_entry.score = game.score_final;
5204   new_entry.time = game.score_time_final;
5205
5206   LoadScore(level_nr);
5207
5208   scores.last_added = addScoreEntry(&scores, &new_entry, one_per_name);
5209
5210   if (scores.last_added >= MAX_SCORE_ENTRIES)
5211   {
5212     scores.last_added = MAX_SCORE_ENTRIES - 1;
5213     scores.force_last_added = TRUE;
5214
5215     scores.entry[scores.last_added] = new_entry;
5216
5217     // store last added local score entry (before merging server scores)
5218     scores.last_added_local = scores.last_added;
5219
5220     return;
5221   }
5222
5223   if (scores.last_added < 0)
5224     return;
5225
5226   SaveScore(level_nr);
5227
5228   // store last added local score entry (before merging server scores)
5229   scores.last_added_local = scores.last_added;
5230
5231   if (!game.LevelSolved_SaveTape)
5232     return;
5233
5234   SaveScoreTape(level_nr);
5235
5236   if (setup.ask_for_using_api_server)
5237   {
5238     setup.use_api_server =
5239       Request("Upload your score and tape to the high score server?", REQ_ASK);
5240
5241     if (!setup.use_api_server)
5242       Request("Not using high score server! Use setup menu to enable again!",
5243               REQ_CONFIRM);
5244
5245     runtime.use_api_server = setup.use_api_server;
5246
5247     // after asking for using API server once, do not ask again
5248     setup.ask_for_using_api_server = FALSE;
5249
5250     SaveSetup_ServerSetup();
5251   }
5252
5253   SaveServerScore(level_nr, tape_saved);
5254 }
5255
5256 void MergeServerScore(void)
5257 {
5258   struct ScoreEntry last_added_entry;
5259   boolean one_per_name = FALSE;
5260   int i;
5261
5262   if (scores.last_added >= 0)
5263     last_added_entry = scores.entry[scores.last_added];
5264
5265   for (i = 0; i < server_scores.num_entries; i++)
5266   {
5267     int pos = addScoreEntry(&scores, &server_scores.entry[i], one_per_name);
5268
5269     if (pos >= 0 && pos <= scores.last_added)
5270       scores.last_added++;
5271   }
5272
5273   if (scores.last_added >= MAX_SCORE_ENTRIES)
5274   {
5275     scores.last_added = MAX_SCORE_ENTRIES - 1;
5276     scores.force_last_added = TRUE;
5277
5278     scores.entry[scores.last_added] = last_added_entry;
5279   }
5280 }
5281
5282 static int getElementMoveStepsizeExt(int x, int y, int direction)
5283 {
5284   int element = Tile[x][y];
5285   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5286   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5287   int horiz_move = (dx != 0);
5288   int sign = (horiz_move ? dx : dy);
5289   int step = sign * element_info[element].move_stepsize;
5290
5291   // special values for move stepsize for spring and things on conveyor belt
5292   if (horiz_move)
5293   {
5294     if (CAN_FALL(element) &&
5295         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Tile[x][y + 1]))
5296       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5297     else if (element == EL_SPRING)
5298       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5299   }
5300
5301   return step;
5302 }
5303
5304 static int getElementMoveStepsize(int x, int y)
5305 {
5306   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5307 }
5308
5309 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5310 {
5311   if (player->GfxAction != action || player->GfxDir != dir)
5312   {
5313     player->GfxAction = action;
5314     player->GfxDir = dir;
5315     player->Frame = 0;
5316     player->StepFrame = 0;
5317   }
5318 }
5319
5320 static void ResetGfxFrame(int x, int y)
5321 {
5322   // profiling showed that "autotest" spends 10~20% of its time in this function
5323   if (DrawingDeactivatedField())
5324     return;
5325
5326   int element = Tile[x][y];
5327   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5328
5329   if (graphic_info[graphic].anim_global_sync)
5330     GfxFrame[x][y] = FrameCounter;
5331   else if (graphic_info[graphic].anim_global_anim_sync)
5332     GfxFrame[x][y] = getGlobalAnimSyncFrame();
5333   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5334     GfxFrame[x][y] = CustomValue[x][y];
5335   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5336     GfxFrame[x][y] = element_info[element].collect_score;
5337   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5338     GfxFrame[x][y] = ChangeDelay[x][y];
5339 }
5340
5341 static void ResetGfxAnimation(int x, int y)
5342 {
5343   GfxAction[x][y] = ACTION_DEFAULT;
5344   GfxDir[x][y] = MovDir[x][y];
5345   GfxFrame[x][y] = 0;
5346
5347   ResetGfxFrame(x, y);
5348 }
5349
5350 static void ResetRandomAnimationValue(int x, int y)
5351 {
5352   GfxRandom[x][y] = INIT_GFX_RANDOM();
5353 }
5354
5355 static void InitMovingField(int x, int y, int direction)
5356 {
5357   int element = Tile[x][y];
5358   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5359   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5360   int newx = x + dx;
5361   int newy = y + dy;
5362   boolean is_moving_before, is_moving_after;
5363
5364   // check if element was/is moving or being moved before/after mode change
5365   is_moving_before = (WasJustMoving[x][y] != 0);
5366   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5367
5368   // reset animation only for moving elements which change direction of moving
5369   // or which just started or stopped moving
5370   // (else CEs with property "can move" / "not moving" are reset each frame)
5371   if (is_moving_before != is_moving_after ||
5372       direction != MovDir[x][y])
5373     ResetGfxAnimation(x, y);
5374
5375   MovDir[x][y] = direction;
5376   GfxDir[x][y] = direction;
5377
5378   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5379                      direction == MV_DOWN && CAN_FALL(element) ?
5380                      ACTION_FALLING : ACTION_MOVING);
5381
5382   // this is needed for CEs with property "can move" / "not moving"
5383
5384   if (is_moving_after)
5385   {
5386     if (Tile[newx][newy] == EL_EMPTY)
5387       Tile[newx][newy] = EL_BLOCKED;
5388
5389     MovDir[newx][newy] = MovDir[x][y];
5390
5391     CustomValue[newx][newy] = CustomValue[x][y];
5392
5393     GfxFrame[newx][newy] = GfxFrame[x][y];
5394     GfxRandom[newx][newy] = GfxRandom[x][y];
5395     GfxAction[newx][newy] = GfxAction[x][y];
5396     GfxDir[newx][newy] = GfxDir[x][y];
5397   }
5398 }
5399
5400 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5401 {
5402   int direction = MovDir[x][y];
5403   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5404   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5405
5406   *goes_to_x = newx;
5407   *goes_to_y = newy;
5408 }
5409
5410 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5411 {
5412   int direction = MovDir[x][y];
5413   int oldx = x + (direction & MV_LEFT ? +1 : direction & MV_RIGHT ? -1 : 0);
5414   int oldy = y + (direction & MV_UP   ? +1 : direction & MV_DOWN  ? -1 : 0);
5415
5416   *comes_from_x = oldx;
5417   *comes_from_y = oldy;
5418 }
5419
5420 static int MovingOrBlocked2Element(int x, int y)
5421 {
5422   int element = Tile[x][y];
5423
5424   if (element == EL_BLOCKED)
5425   {
5426     int oldx, oldy;
5427
5428     Blocked2Moving(x, y, &oldx, &oldy);
5429
5430     return Tile[oldx][oldy];
5431   }
5432
5433   return element;
5434 }
5435
5436 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5437 {
5438   // like MovingOrBlocked2Element(), but if element is moving
5439   // and (x, y) is the field the moving element is just leaving,
5440   // return EL_BLOCKED instead of the element value
5441   int element = Tile[x][y];
5442
5443   if (IS_MOVING(x, y))
5444   {
5445     if (element == EL_BLOCKED)
5446     {
5447       int oldx, oldy;
5448
5449       Blocked2Moving(x, y, &oldx, &oldy);
5450       return Tile[oldx][oldy];
5451     }
5452     else
5453       return EL_BLOCKED;
5454   }
5455   else
5456     return element;
5457 }
5458
5459 static void RemoveField(int x, int y)
5460 {
5461   Tile[x][y] = EL_EMPTY;
5462
5463   MovPos[x][y] = 0;
5464   MovDir[x][y] = 0;
5465   MovDelay[x][y] = 0;
5466
5467   CustomValue[x][y] = 0;
5468
5469   AmoebaNr[x][y] = 0;
5470   ChangeDelay[x][y] = 0;
5471   ChangePage[x][y] = -1;
5472   Pushed[x][y] = FALSE;
5473
5474   GfxElement[x][y] = EL_UNDEFINED;
5475   GfxAction[x][y] = ACTION_DEFAULT;
5476   GfxDir[x][y] = MV_NONE;
5477 }
5478
5479 static void RemoveMovingField(int x, int y)
5480 {
5481   int oldx = x, oldy = y, newx = x, newy = y;
5482   int element = Tile[x][y];
5483   int next_element = EL_UNDEFINED;
5484
5485   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5486     return;
5487
5488   if (IS_MOVING(x, y))
5489   {
5490     Moving2Blocked(x, y, &newx, &newy);
5491
5492     if (Tile[newx][newy] != EL_BLOCKED)
5493     {
5494       // element is moving, but target field is not free (blocked), but
5495       // already occupied by something different (example: acid pool);
5496       // in this case, only remove the moving field, but not the target
5497
5498       RemoveField(oldx, oldy);
5499
5500       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5501
5502       TEST_DrawLevelField(oldx, oldy);
5503
5504       return;
5505     }
5506   }
5507   else if (element == EL_BLOCKED)
5508   {
5509     Blocked2Moving(x, y, &oldx, &oldy);
5510     if (!IS_MOVING(oldx, oldy))
5511       return;
5512   }
5513
5514   if (element == EL_BLOCKED &&
5515       (Tile[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5516        Tile[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5517        Tile[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5518        Tile[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5519        Tile[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5520        Tile[oldx][oldy] == EL_AMOEBA_DROPPING))
5521     next_element = get_next_element(Tile[oldx][oldy]);
5522
5523   RemoveField(oldx, oldy);
5524   RemoveField(newx, newy);
5525
5526   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5527
5528   if (next_element != EL_UNDEFINED)
5529     Tile[oldx][oldy] = next_element;
5530
5531   TEST_DrawLevelField(oldx, oldy);
5532   TEST_DrawLevelField(newx, newy);
5533 }
5534
5535 void DrawDynamite(int x, int y)
5536 {
5537   int sx = SCREENX(x), sy = SCREENY(y);
5538   int graphic = el2img(Tile[x][y]);
5539   int frame;
5540
5541   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5542     return;
5543
5544   if (IS_WALKABLE_INSIDE(Back[x][y]))
5545     return;
5546
5547   if (Back[x][y])
5548     DrawLevelElement(x, y, Back[x][y]);
5549   else if (Store[x][y])
5550     DrawLevelElement(x, y, Store[x][y]);
5551   else if (game.use_masked_elements)
5552     DrawLevelElement(x, y, EL_EMPTY);
5553
5554   frame = getGraphicAnimationFrameXY(graphic, x, y);
5555
5556   if (Back[x][y] || Store[x][y] || game.use_masked_elements)
5557     DrawGraphicThruMask(sx, sy, graphic, frame);
5558   else
5559     DrawGraphic(sx, sy, graphic, frame);
5560 }
5561
5562 static void CheckDynamite(int x, int y)
5563 {
5564   if (MovDelay[x][y] != 0)      // dynamite is still waiting to explode
5565   {
5566     MovDelay[x][y]--;
5567
5568     if (MovDelay[x][y] != 0)
5569     {
5570       DrawDynamite(x, y);
5571       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5572
5573       return;
5574     }
5575   }
5576
5577   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5578
5579   Bang(x, y);
5580 }
5581
5582 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5583 {
5584   boolean num_checked_players = 0;
5585   int i;
5586
5587   for (i = 0; i < MAX_PLAYERS; i++)
5588   {
5589     if (stored_player[i].active)
5590     {
5591       int sx = stored_player[i].jx;
5592       int sy = stored_player[i].jy;
5593
5594       if (num_checked_players == 0)
5595       {
5596         *sx1 = *sx2 = sx;
5597         *sy1 = *sy2 = sy;
5598       }
5599       else
5600       {
5601         *sx1 = MIN(*sx1, sx);
5602         *sy1 = MIN(*sy1, sy);
5603         *sx2 = MAX(*sx2, sx);
5604         *sy2 = MAX(*sy2, sy);
5605       }
5606
5607       num_checked_players++;
5608     }
5609   }
5610 }
5611
5612 static boolean checkIfAllPlayersFitToScreen_RND(void)
5613 {
5614   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5615
5616   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5617
5618   return (sx2 - sx1 < SCR_FIELDX &&
5619           sy2 - sy1 < SCR_FIELDY);
5620 }
5621
5622 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5623 {
5624   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5625
5626   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5627
5628   *sx = (sx1 + sx2) / 2;
5629   *sy = (sy1 + sy2) / 2;
5630 }
5631
5632 static void DrawRelocateScreen(int old_x, int old_y, int x, int y,
5633                                boolean center_screen, boolean quick_relocation)
5634 {
5635   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5636   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5637   boolean no_delay = (tape.warp_forward);
5638   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5639   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5640   int new_scroll_x, new_scroll_y;
5641
5642   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5643   {
5644     // case 1: quick relocation inside visible screen (without scrolling)
5645
5646     RedrawPlayfield();
5647
5648     return;
5649   }
5650
5651   if (!level.shifted_relocation || center_screen)
5652   {
5653     // relocation _with_ centering of screen
5654
5655     new_scroll_x = SCROLL_POSITION_X(x);
5656     new_scroll_y = SCROLL_POSITION_Y(y);
5657   }
5658   else
5659   {
5660     // relocation _without_ centering of screen
5661
5662     int center_scroll_x = SCROLL_POSITION_X(old_x);
5663     int center_scroll_y = SCROLL_POSITION_Y(old_y);
5664     int offset_x = x + (scroll_x - center_scroll_x);
5665     int offset_y = y + (scroll_y - center_scroll_y);
5666
5667     // for new screen position, apply previous offset to center position
5668     new_scroll_x = SCROLL_POSITION_X(offset_x);
5669     new_scroll_y = SCROLL_POSITION_Y(offset_y);
5670   }
5671
5672   if (quick_relocation)
5673   {
5674     // case 2: quick relocation (redraw without visible scrolling)
5675
5676     scroll_x = new_scroll_x;
5677     scroll_y = new_scroll_y;
5678
5679     RedrawPlayfield();
5680
5681     return;
5682   }
5683
5684   // case 3: visible relocation (with scrolling to new position)
5685
5686   ScrollScreen(NULL, SCROLL_GO_ON);     // scroll last frame to full tile
5687
5688   SetVideoFrameDelay(wait_delay_value);
5689
5690   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5691   {
5692     int dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5693     int dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5694
5695     if (dx == 0 && dy == 0)             // no scrolling needed at all
5696       break;
5697
5698     scroll_x -= dx;
5699     scroll_y -= dy;
5700
5701     // set values for horizontal/vertical screen scrolling (half tile size)
5702     int dir_x = (dx != 0 ? MV_HORIZONTAL : 0);
5703     int dir_y = (dy != 0 ? MV_VERTICAL   : 0);
5704     int pos_x = dx * TILEX / 2;
5705     int pos_y = dy * TILEY / 2;
5706     int fx = getFieldbufferOffsetX_RND(dir_x, pos_x);
5707     int fy = getFieldbufferOffsetY_RND(dir_y, pos_y);
5708
5709     ScrollLevel(dx, dy);
5710     DrawAllPlayers();
5711
5712     // scroll in two steps of half tile size to make things smoother
5713     BlitScreenToBitmapExt_RND(window, fx, fy);
5714
5715     // scroll second step to align at full tile size
5716     BlitScreenToBitmap(window);
5717   }
5718
5719   DrawAllPlayers();
5720   BackToFront();
5721
5722   SetVideoFrameDelay(frame_delay_value_old);
5723 }
5724
5725 static void RelocatePlayer(int jx, int jy, int el_player_raw)
5726 {
5727   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5728   int player_nr = GET_PLAYER_NR(el_player);
5729   struct PlayerInfo *player = &stored_player[player_nr];
5730   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5731   boolean no_delay = (tape.warp_forward);
5732   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5733   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5734   int old_jx = player->jx;
5735   int old_jy = player->jy;
5736   int old_element = Tile[old_jx][old_jy];
5737   int element = Tile[jx][jy];
5738   boolean player_relocated = (old_jx != jx || old_jy != jy);
5739
5740   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5741   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5742   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5743   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5744   int leave_side_horiz = move_dir_horiz;
5745   int leave_side_vert  = move_dir_vert;
5746   int enter_side = enter_side_horiz | enter_side_vert;
5747   int leave_side = leave_side_horiz | leave_side_vert;
5748
5749   if (player->buried)           // do not reanimate dead player
5750     return;
5751
5752   if (!player_relocated)        // no need to relocate the player
5753     return;
5754
5755   if (IS_PLAYER(jx, jy))        // player already placed at new position
5756   {
5757     RemoveField(jx, jy);        // temporarily remove newly placed player
5758     DrawLevelField(jx, jy);
5759   }
5760
5761   if (player->present)
5762   {
5763     while (player->MovPos)
5764     {
5765       ScrollPlayer(player, SCROLL_GO_ON);
5766       ScrollScreen(NULL, SCROLL_GO_ON);
5767
5768       AdvanceFrameAndPlayerCounters(player->index_nr);
5769
5770       DrawPlayer(player);
5771
5772       BackToFront_WithFrameDelay(wait_delay_value);
5773     }
5774
5775     DrawPlayer(player);         // needed here only to cleanup last field
5776     DrawLevelField(player->jx, player->jy);     // remove player graphic
5777
5778     player->is_moving = FALSE;
5779   }
5780
5781   if (IS_CUSTOM_ELEMENT(old_element))
5782     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5783                                CE_LEFT_BY_PLAYER,
5784                                player->index_bit, leave_side);
5785
5786   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5787                                       CE_PLAYER_LEAVES_X,
5788                                       player->index_bit, leave_side);
5789
5790   Tile[jx][jy] = el_player;
5791   InitPlayerField(jx, jy, el_player, TRUE);
5792
5793   /* "InitPlayerField()" above sets Tile[jx][jy] to EL_EMPTY, but it may be
5794      possible that the relocation target field did not contain a player element,
5795      but a walkable element, to which the new player was relocated -- in this
5796      case, restore that (already initialized!) element on the player field */
5797   if (!IS_PLAYER_ELEMENT(element))      // player may be set on walkable element
5798   {
5799     Tile[jx][jy] = element;     // restore previously existing element
5800   }
5801
5802   // only visually relocate centered player
5803   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy,
5804                      FALSE, level.instant_relocation);
5805
5806   TestIfPlayerTouchesBadThing(jx, jy);
5807   TestIfPlayerTouchesCustomElement(jx, jy);
5808
5809   if (IS_CUSTOM_ELEMENT(element))
5810     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5811                                player->index_bit, enter_side);
5812
5813   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5814                                       player->index_bit, enter_side);
5815
5816   if (player->is_switching)
5817   {
5818     /* ensure that relocation while still switching an element does not cause
5819        a new element to be treated as also switched directly after relocation
5820        (this is important for teleporter switches that teleport the player to
5821        a place where another teleporter switch is in the same direction, which
5822        would then incorrectly be treated as immediately switched before the
5823        direction key that caused the switch was released) */
5824
5825     player->switch_x += jx - old_jx;
5826     player->switch_y += jy - old_jy;
5827   }
5828 }
5829
5830 static void Explode(int ex, int ey, int phase, int mode)
5831 {
5832   int x, y;
5833   int last_phase;
5834   int border_element;
5835
5836   if (game.explosions_delayed)
5837   {
5838     ExplodeField[ex][ey] = mode;
5839     return;
5840   }
5841
5842   if (phase == EX_PHASE_START)          // initialize 'Store[][]' field
5843   {
5844     int center_element = Tile[ex][ey];
5845     int ce_value = CustomValue[ex][ey];
5846     int ce_score = element_info[center_element].collect_score;
5847     int artwork_element, explosion_element;     // set these values later
5848
5849     // remove things displayed in background while burning dynamite
5850     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5851       Back[ex][ey] = 0;
5852
5853     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5854     {
5855       // put moving element to center field (and let it explode there)
5856       center_element = MovingOrBlocked2Element(ex, ey);
5857       RemoveMovingField(ex, ey);
5858       Tile[ex][ey] = center_element;
5859     }
5860
5861     // now "center_element" is finally determined -- set related values now
5862     artwork_element = center_element;           // for custom player artwork
5863     explosion_element = center_element;         // for custom player artwork
5864
5865     if (IS_PLAYER(ex, ey))
5866     {
5867       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5868
5869       artwork_element = stored_player[player_nr].artwork_element;
5870
5871       if (level.use_explosion_element[player_nr])
5872       {
5873         explosion_element = level.explosion_element[player_nr];
5874         artwork_element = explosion_element;
5875       }
5876     }
5877
5878     if (mode == EX_TYPE_NORMAL ||
5879         mode == EX_TYPE_CENTER ||
5880         mode == EX_TYPE_CROSS)
5881       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5882
5883     last_phase = element_info[explosion_element].explosion_delay + 1;
5884
5885     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5886     {
5887       int xx = x - ex + 1;
5888       int yy = y - ey + 1;
5889       int element;
5890
5891       if (!IN_LEV_FIELD(x, y) ||
5892           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5893           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5894         continue;
5895
5896       element = Tile[x][y];
5897
5898       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5899       {
5900         element = MovingOrBlocked2Element(x, y);
5901
5902         if (!IS_EXPLOSION_PROOF(element))
5903           RemoveMovingField(x, y);
5904       }
5905
5906       // indestructible elements can only explode in center (but not flames)
5907       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5908                                            mode == EX_TYPE_BORDER)) ||
5909           element == EL_FLAMES)
5910         continue;
5911
5912       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5913          behaviour, for example when touching a yamyam that explodes to rocks
5914          with active deadly shield, a rock is created under the player !!! */
5915       // (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8)
5916 #if 0
5917       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5918           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5919            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5920 #else
5921       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5922 #endif
5923       {
5924         if (IS_ACTIVE_BOMB(element))
5925         {
5926           // re-activate things under the bomb like gate or penguin
5927           Tile[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5928           Back[x][y] = 0;
5929         }
5930
5931         continue;
5932       }
5933
5934       // save walkable background elements while explosion on same tile
5935       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5936           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5937         Back[x][y] = element;
5938
5939       // ignite explodable elements reached by other explosion
5940       if (element == EL_EXPLOSION)
5941         element = Store2[x][y];
5942
5943       if (AmoebaNr[x][y] &&
5944           (element == EL_AMOEBA_FULL ||
5945            element == EL_BD_AMOEBA ||
5946            element == EL_AMOEBA_GROWING))
5947       {
5948         AmoebaCnt[AmoebaNr[x][y]]--;
5949         AmoebaCnt2[AmoebaNr[x][y]]--;
5950       }
5951
5952       RemoveField(x, y);
5953
5954       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5955       {
5956         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5957
5958         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5959
5960         if (PLAYERINFO(ex, ey)->use_murphy)
5961           Store[x][y] = EL_EMPTY;
5962       }
5963
5964       // !!! check this case -- currently needed for rnd_rado_negundo_v,
5965       // !!! levels 015 018 019 020 021 022 023 026 027 028 !!!
5966       else if (IS_PLAYER_ELEMENT(center_element))
5967         Store[x][y] = EL_EMPTY;
5968       else if (center_element == EL_YAMYAM)
5969         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5970       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5971         Store[x][y] = element_info[center_element].content.e[xx][yy];
5972 #if 1
5973       // needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5974       // (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5975       // otherwise) -- FIX THIS !!!
5976       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5977         Store[x][y] = element_info[element].content.e[1][1];
5978 #else
5979       else if (!CAN_EXPLODE(element))
5980         Store[x][y] = element_info[element].content.e[1][1];
5981 #endif
5982       else
5983         Store[x][y] = EL_EMPTY;
5984
5985       if (IS_CUSTOM_ELEMENT(center_element))
5986         Store[x][y] = (Store[x][y] == EL_CURRENT_CE_VALUE ? ce_value :
5987                        Store[x][y] == EL_CURRENT_CE_SCORE ? ce_score :
5988                        Store[x][y] >= EL_PREV_CE_8 &&
5989                        Store[x][y] <= EL_NEXT_CE_8 ?
5990                        RESOLVED_REFERENCE_ELEMENT(center_element, Store[x][y]) :
5991                        Store[x][y]);
5992
5993       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5994           center_element == EL_AMOEBA_TO_DIAMOND)
5995         Store2[x][y] = element;
5996
5997       Tile[x][y] = EL_EXPLOSION;
5998       GfxElement[x][y] = artwork_element;
5999
6000       ExplodePhase[x][y] = 1;
6001       ExplodeDelay[x][y] = last_phase;
6002
6003       Stop[x][y] = TRUE;
6004     }
6005
6006     if (center_element == EL_YAMYAM)
6007       game.yamyam_content_nr =
6008         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
6009
6010     return;
6011   }
6012
6013   if (Stop[ex][ey])
6014     return;
6015
6016   x = ex;
6017   y = ey;
6018
6019   if (phase == 1)
6020     GfxFrame[x][y] = 0;         // restart explosion animation
6021
6022   last_phase = ExplodeDelay[x][y];
6023
6024   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
6025
6026   // this can happen if the player leaves an explosion just in time
6027   if (GfxElement[x][y] == EL_UNDEFINED)
6028     GfxElement[x][y] = EL_EMPTY;
6029
6030   border_element = Store2[x][y];
6031   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6032     border_element = StorePlayer[x][y];
6033
6034   if (phase == element_info[border_element].ignition_delay ||
6035       phase == last_phase)
6036   {
6037     boolean border_explosion = FALSE;
6038
6039     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
6040         !PLAYER_EXPLOSION_PROTECTED(x, y))
6041     {
6042       KillPlayerUnlessExplosionProtected(x, y);
6043       border_explosion = TRUE;
6044     }
6045     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
6046     {
6047       Tile[x][y] = Store2[x][y];
6048       Store2[x][y] = 0;
6049       Bang(x, y);
6050       border_explosion = TRUE;
6051     }
6052     else if (border_element == EL_AMOEBA_TO_DIAMOND)
6053     {
6054       AmoebaToDiamond(x, y);
6055       Store2[x][y] = 0;
6056       border_explosion = TRUE;
6057     }
6058
6059     // if an element just explodes due to another explosion (chain-reaction),
6060     // do not immediately end the new explosion when it was the last frame of
6061     // the explosion (as it would be done in the following "if"-statement!)
6062     if (border_explosion && phase == last_phase)
6063       return;
6064   }
6065
6066   // this can happen if the player was just killed by an explosion
6067   if (GfxElement[x][y] == EL_UNDEFINED)
6068     GfxElement[x][y] = EL_EMPTY;
6069
6070   if (phase == last_phase)
6071   {
6072     int element;
6073
6074     element = Tile[x][y] = Store[x][y];
6075     Store[x][y] = Store2[x][y] = 0;
6076     GfxElement[x][y] = EL_UNDEFINED;
6077
6078     // player can escape from explosions and might therefore be still alive
6079     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
6080         element <= EL_PLAYER_IS_EXPLODING_4)
6081     {
6082       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
6083       int explosion_element = EL_PLAYER_1 + player_nr;
6084       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
6085       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
6086
6087       if (level.use_explosion_element[player_nr])
6088         explosion_element = level.explosion_element[player_nr];
6089
6090       Tile[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
6091                     element_info[explosion_element].content.e[xx][yy]);
6092     }
6093
6094     // restore probably existing indestructible background element
6095     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
6096       element = Tile[x][y] = Back[x][y];
6097     Back[x][y] = 0;
6098
6099     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
6100     GfxDir[x][y] = MV_NONE;
6101     ChangeDelay[x][y] = 0;
6102     ChangePage[x][y] = -1;
6103
6104     CustomValue[x][y] = 0;
6105
6106     InitField_WithBug2(x, y, FALSE);
6107
6108     TEST_DrawLevelField(x, y);
6109
6110     TestIfElementTouchesCustomElement(x, y);
6111
6112     if (GFX_CRUMBLED(element))
6113       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6114
6115     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
6116       StorePlayer[x][y] = 0;
6117
6118     if (IS_PLAYER_ELEMENT(element))
6119       RelocatePlayer(x, y, element);
6120   }
6121   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6122   {
6123     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
6124     int frame = getGraphicAnimationFrameXY(graphic, x, y);
6125
6126     if (phase == 1)
6127       TEST_DrawLevelFieldCrumbled(x, y);
6128
6129     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
6130     {
6131       DrawLevelElement(x, y, Back[x][y]);
6132       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
6133     }
6134     else if (IS_WALKABLE_UNDER(Back[x][y]))
6135     {
6136       DrawLevelGraphic(x, y, graphic, frame);
6137       DrawLevelElementThruMask(x, y, Back[x][y]);
6138     }
6139     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
6140       DrawLevelGraphic(x, y, graphic, frame);
6141   }
6142 }
6143
6144 static void DynaExplode(int ex, int ey)
6145 {
6146   int i, j;
6147   int dynabomb_element = Tile[ex][ey];
6148   int dynabomb_size = 1;
6149   boolean dynabomb_xl = FALSE;
6150   struct PlayerInfo *player;
6151   struct XY *xy = xy_topdown;
6152
6153   if (IS_ACTIVE_BOMB(dynabomb_element))
6154   {
6155     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
6156     dynabomb_size = player->dynabomb_size;
6157     dynabomb_xl = player->dynabomb_xl;
6158     player->dynabombs_left++;
6159   }
6160
6161   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6162
6163   for (i = 0; i < NUM_DIRECTIONS; i++)
6164   {
6165     for (j = 1; j <= dynabomb_size; j++)
6166     {
6167       int x = ex + j * xy[i].x;
6168       int y = ey + j * xy[i].y;
6169       int element;
6170
6171       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Tile[x][y]))
6172         break;
6173
6174       element = Tile[x][y];
6175
6176       // do not restart explosions of fields with active bombs
6177       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6178         continue;
6179
6180       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6181
6182       if (element != EL_EMPTY && element != EL_EXPLOSION &&
6183           !IS_DIGGABLE(element) && !dynabomb_xl)
6184         break;
6185     }
6186   }
6187 }
6188
6189 void Bang(int x, int y)
6190 {
6191   int element = MovingOrBlocked2Element(x, y);
6192   int explosion_type = EX_TYPE_NORMAL;
6193
6194   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6195   {
6196     struct PlayerInfo *player = PLAYERINFO(x, y);
6197
6198     element = Tile[x][y] = player->initial_element;
6199
6200     if (level.use_explosion_element[player->index_nr])
6201     {
6202       int explosion_element = level.explosion_element[player->index_nr];
6203
6204       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6205         explosion_type = EX_TYPE_CROSS;
6206       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6207         explosion_type = EX_TYPE_CENTER;
6208     }
6209   }
6210
6211   switch (element)
6212   {
6213     case EL_BUG:
6214     case EL_SPACESHIP:
6215     case EL_BD_BUTTERFLY:
6216     case EL_BD_FIREFLY:
6217     case EL_YAMYAM:
6218     case EL_DARK_YAMYAM:
6219     case EL_ROBOT:
6220     case EL_PACMAN:
6221     case EL_MOLE:
6222       RaiseScoreElement(element);
6223       break;
6224
6225     case EL_DYNABOMB_PLAYER_1_ACTIVE:
6226     case EL_DYNABOMB_PLAYER_2_ACTIVE:
6227     case EL_DYNABOMB_PLAYER_3_ACTIVE:
6228     case EL_DYNABOMB_PLAYER_4_ACTIVE:
6229     case EL_DYNABOMB_INCREASE_NUMBER:
6230     case EL_DYNABOMB_INCREASE_SIZE:
6231     case EL_DYNABOMB_INCREASE_POWER:
6232       explosion_type = EX_TYPE_DYNA;
6233       break;
6234
6235     case EL_DC_LANDMINE:
6236       explosion_type = EX_TYPE_CENTER;
6237       break;
6238
6239     case EL_PENGUIN:
6240     case EL_LAMP:
6241     case EL_LAMP_ACTIVE:
6242     case EL_AMOEBA_TO_DIAMOND:
6243       if (!IS_PLAYER(x, y))     // penguin and player may be at same field
6244         explosion_type = EX_TYPE_CENTER;
6245       break;
6246
6247     default:
6248       if (element_info[element].explosion_type == EXPLODES_CROSS)
6249         explosion_type = EX_TYPE_CROSS;
6250       else if (element_info[element].explosion_type == EXPLODES_1X1)
6251         explosion_type = EX_TYPE_CENTER;
6252       break;
6253   }
6254
6255   if (explosion_type == EX_TYPE_DYNA)
6256     DynaExplode(x, y);
6257   else
6258     Explode(x, y, EX_PHASE_START, explosion_type);
6259
6260   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6261 }
6262
6263 static void SplashAcid(int x, int y)
6264 {
6265   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6266       (!IN_LEV_FIELD(x - 1, y - 2) ||
6267        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6268     Tile[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6269
6270   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6271       (!IN_LEV_FIELD(x + 1, y - 2) ||
6272        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6273     Tile[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6274
6275   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6276 }
6277
6278 static void InitBeltMovement(void)
6279 {
6280   static int belt_base_element[4] =
6281   {
6282     EL_CONVEYOR_BELT_1_LEFT,
6283     EL_CONVEYOR_BELT_2_LEFT,
6284     EL_CONVEYOR_BELT_3_LEFT,
6285     EL_CONVEYOR_BELT_4_LEFT
6286   };
6287   static int belt_base_active_element[4] =
6288   {
6289     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6290     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6291     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6292     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6293   };
6294
6295   int x, y, i, j;
6296
6297   // set frame order for belt animation graphic according to belt direction
6298   for (i = 0; i < NUM_BELTS; i++)
6299   {
6300     int belt_nr = i;
6301
6302     for (j = 0; j < NUM_BELT_PARTS; j++)
6303     {
6304       int element = belt_base_active_element[belt_nr] + j;
6305       int graphic_1 = el2img(element);
6306       int graphic_2 = el2panelimg(element);
6307
6308       if (game.belt_dir[i] == MV_LEFT)
6309       {
6310         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6311         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6312       }
6313       else
6314       {
6315         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6316         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6317       }
6318     }
6319   }
6320
6321   SCAN_PLAYFIELD(x, y)
6322   {
6323     int element = Tile[x][y];
6324
6325     for (i = 0; i < NUM_BELTS; i++)
6326     {
6327       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6328       {
6329         int e_belt_nr = getBeltNrFromBeltElement(element);
6330         int belt_nr = i;
6331
6332         if (e_belt_nr == belt_nr)
6333         {
6334           int belt_part = Tile[x][y] - belt_base_element[belt_nr];
6335
6336           Tile[x][y] = belt_base_active_element[belt_nr] + belt_part;
6337         }
6338       }
6339     }
6340   }
6341 }
6342
6343 static void ToggleBeltSwitch(int x, int y)
6344 {
6345   static int belt_base_element[4] =
6346   {
6347     EL_CONVEYOR_BELT_1_LEFT,
6348     EL_CONVEYOR_BELT_2_LEFT,
6349     EL_CONVEYOR_BELT_3_LEFT,
6350     EL_CONVEYOR_BELT_4_LEFT
6351   };
6352   static int belt_base_active_element[4] =
6353   {
6354     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6355     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6356     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6357     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6358   };
6359   static int belt_base_switch_element[4] =
6360   {
6361     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6362     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6363     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6364     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6365   };
6366   static int belt_move_dir[4] =
6367   {
6368     MV_LEFT,
6369     MV_NONE,
6370     MV_RIGHT,
6371     MV_NONE,
6372   };
6373
6374   int element = Tile[x][y];
6375   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6376   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6377   int belt_dir = belt_move_dir[belt_dir_nr];
6378   int xx, yy, i;
6379
6380   if (!IS_BELT_SWITCH(element))
6381     return;
6382
6383   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6384   game.belt_dir[belt_nr] = belt_dir;
6385
6386   if (belt_dir_nr == 3)
6387     belt_dir_nr = 1;
6388
6389   // set frame order for belt animation graphic according to belt direction
6390   for (i = 0; i < NUM_BELT_PARTS; i++)
6391   {
6392     int element = belt_base_active_element[belt_nr] + i;
6393     int graphic_1 = el2img(element);
6394     int graphic_2 = el2panelimg(element);
6395
6396     if (belt_dir == MV_LEFT)
6397     {
6398       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6399       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6400     }
6401     else
6402     {
6403       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6404       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6405     }
6406   }
6407
6408   SCAN_PLAYFIELD(xx, yy)
6409   {
6410     int element = Tile[xx][yy];
6411
6412     if (IS_BELT_SWITCH(element))
6413     {
6414       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6415
6416       if (e_belt_nr == belt_nr)
6417       {
6418         Tile[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6419         TEST_DrawLevelField(xx, yy);
6420       }
6421     }
6422     else if (IS_BELT(element) && belt_dir != MV_NONE)
6423     {
6424       int e_belt_nr = getBeltNrFromBeltElement(element);
6425
6426       if (e_belt_nr == belt_nr)
6427       {
6428         int belt_part = Tile[xx][yy] - belt_base_element[belt_nr];
6429
6430         Tile[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6431         TEST_DrawLevelField(xx, yy);
6432       }
6433     }
6434     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6435     {
6436       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6437
6438       if (e_belt_nr == belt_nr)
6439       {
6440         int belt_part = Tile[xx][yy] - belt_base_active_element[belt_nr];
6441
6442         Tile[xx][yy] = belt_base_element[belt_nr] + belt_part;
6443         TEST_DrawLevelField(xx, yy);
6444       }
6445     }
6446   }
6447 }
6448
6449 static void ToggleSwitchgateSwitch(void)
6450 {
6451   int xx, yy;
6452
6453   game.switchgate_pos = !game.switchgate_pos;
6454
6455   SCAN_PLAYFIELD(xx, yy)
6456   {
6457     int element = Tile[xx][yy];
6458
6459     if (element == EL_SWITCHGATE_SWITCH_UP)
6460     {
6461       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6462       TEST_DrawLevelField(xx, yy);
6463     }
6464     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6465     {
6466       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6467       TEST_DrawLevelField(xx, yy);
6468     }
6469     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6470     {
6471       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6472       TEST_DrawLevelField(xx, yy);
6473     }
6474     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6475     {
6476       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6477       TEST_DrawLevelField(xx, yy);
6478     }
6479     else if (element == EL_SWITCHGATE_OPEN ||
6480              element == EL_SWITCHGATE_OPENING)
6481     {
6482       Tile[xx][yy] = EL_SWITCHGATE_CLOSING;
6483
6484       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6485     }
6486     else if (element == EL_SWITCHGATE_CLOSED ||
6487              element == EL_SWITCHGATE_CLOSING)
6488     {
6489       Tile[xx][yy] = EL_SWITCHGATE_OPENING;
6490
6491       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6492     }
6493   }
6494 }
6495
6496 static int getInvisibleActiveFromInvisibleElement(int element)
6497 {
6498   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6499           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6500           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6501           element);
6502 }
6503
6504 static int getInvisibleFromInvisibleActiveElement(int element)
6505 {
6506   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6507           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6508           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6509           element);
6510 }
6511
6512 static void RedrawAllLightSwitchesAndInvisibleElements(void)
6513 {
6514   int x, y;
6515
6516   SCAN_PLAYFIELD(x, y)
6517   {
6518     int element = Tile[x][y];
6519
6520     if (element == EL_LIGHT_SWITCH &&
6521         game.light_time_left > 0)
6522     {
6523       Tile[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6524       TEST_DrawLevelField(x, y);
6525     }
6526     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6527              game.light_time_left == 0)
6528     {
6529       Tile[x][y] = EL_LIGHT_SWITCH;
6530       TEST_DrawLevelField(x, y);
6531     }
6532     else if (element == EL_EMC_DRIPPER &&
6533              game.light_time_left > 0)
6534     {
6535       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6536       TEST_DrawLevelField(x, y);
6537     }
6538     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6539              game.light_time_left == 0)
6540     {
6541       Tile[x][y] = EL_EMC_DRIPPER;
6542       TEST_DrawLevelField(x, y);
6543     }
6544     else if (element == EL_INVISIBLE_STEELWALL ||
6545              element == EL_INVISIBLE_WALL ||
6546              element == EL_INVISIBLE_SAND)
6547     {
6548       if (game.light_time_left > 0)
6549         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6550
6551       TEST_DrawLevelField(x, y);
6552
6553       // uncrumble neighbour fields, if needed
6554       if (element == EL_INVISIBLE_SAND)
6555         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6556     }
6557     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6558              element == EL_INVISIBLE_WALL_ACTIVE ||
6559              element == EL_INVISIBLE_SAND_ACTIVE)
6560     {
6561       if (game.light_time_left == 0)
6562         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6563
6564       TEST_DrawLevelField(x, y);
6565
6566       // re-crumble neighbour fields, if needed
6567       if (element == EL_INVISIBLE_SAND)
6568         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6569     }
6570   }
6571 }
6572
6573 static void RedrawAllInvisibleElementsForLenses(void)
6574 {
6575   int x, y;
6576
6577   SCAN_PLAYFIELD(x, y)
6578   {
6579     int element = Tile[x][y];
6580
6581     if (element == EL_EMC_DRIPPER &&
6582         game.lenses_time_left > 0)
6583     {
6584       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6585       TEST_DrawLevelField(x, y);
6586     }
6587     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6588              game.lenses_time_left == 0)
6589     {
6590       Tile[x][y] = EL_EMC_DRIPPER;
6591       TEST_DrawLevelField(x, y);
6592     }
6593     else if (element == EL_INVISIBLE_STEELWALL ||
6594              element == EL_INVISIBLE_WALL ||
6595              element == EL_INVISIBLE_SAND)
6596     {
6597       if (game.lenses_time_left > 0)
6598         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6599
6600       TEST_DrawLevelField(x, y);
6601
6602       // uncrumble neighbour fields, if needed
6603       if (element == EL_INVISIBLE_SAND)
6604         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6605     }
6606     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6607              element == EL_INVISIBLE_WALL_ACTIVE ||
6608              element == EL_INVISIBLE_SAND_ACTIVE)
6609     {
6610       if (game.lenses_time_left == 0)
6611         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6612
6613       TEST_DrawLevelField(x, y);
6614
6615       // re-crumble neighbour fields, if needed
6616       if (element == EL_INVISIBLE_SAND)
6617         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6618     }
6619   }
6620 }
6621
6622 static void RedrawAllInvisibleElementsForMagnifier(void)
6623 {
6624   int x, y;
6625
6626   SCAN_PLAYFIELD(x, y)
6627   {
6628     int element = Tile[x][y];
6629
6630     if (element == EL_EMC_FAKE_GRASS &&
6631         game.magnify_time_left > 0)
6632     {
6633       Tile[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6634       TEST_DrawLevelField(x, y);
6635     }
6636     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6637              game.magnify_time_left == 0)
6638     {
6639       Tile[x][y] = EL_EMC_FAKE_GRASS;
6640       TEST_DrawLevelField(x, y);
6641     }
6642     else if (IS_GATE_GRAY(element) &&
6643              game.magnify_time_left > 0)
6644     {
6645       Tile[x][y] = (IS_RND_GATE_GRAY(element) ?
6646                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6647                     IS_EM_GATE_GRAY(element) ?
6648                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6649                     IS_EMC_GATE_GRAY(element) ?
6650                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6651                     IS_DC_GATE_GRAY(element) ?
6652                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6653                     element);
6654       TEST_DrawLevelField(x, y);
6655     }
6656     else if (IS_GATE_GRAY_ACTIVE(element) &&
6657              game.magnify_time_left == 0)
6658     {
6659       Tile[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6660                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6661                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6662                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6663                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6664                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6665                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6666                     EL_DC_GATE_WHITE_GRAY :
6667                     element);
6668       TEST_DrawLevelField(x, y);
6669     }
6670   }
6671 }
6672
6673 static void ToggleLightSwitch(int x, int y)
6674 {
6675   int element = Tile[x][y];
6676
6677   game.light_time_left =
6678     (element == EL_LIGHT_SWITCH ?
6679      level.time_light * FRAMES_PER_SECOND : 0);
6680
6681   RedrawAllLightSwitchesAndInvisibleElements();
6682 }
6683
6684 static void ActivateTimegateSwitch(int x, int y)
6685 {
6686   int xx, yy;
6687
6688   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6689
6690   SCAN_PLAYFIELD(xx, yy)
6691   {
6692     int element = Tile[xx][yy];
6693
6694     if (element == EL_TIMEGATE_CLOSED ||
6695         element == EL_TIMEGATE_CLOSING)
6696     {
6697       Tile[xx][yy] = EL_TIMEGATE_OPENING;
6698       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6699     }
6700
6701     /*
6702     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6703     {
6704       Tile[xx][yy] = EL_TIMEGATE_SWITCH;
6705       TEST_DrawLevelField(xx, yy);
6706     }
6707     */
6708
6709   }
6710
6711   Tile[x][y] = (Tile[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6712                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6713 }
6714
6715 static void Impact(int x, int y)
6716 {
6717   boolean last_line = (y == lev_fieldy - 1);
6718   boolean object_hit = FALSE;
6719   boolean impact = (last_line || object_hit);
6720   int element = Tile[x][y];
6721   int smashed = EL_STEELWALL;
6722
6723   if (!last_line)       // check if element below was hit
6724   {
6725     if (Tile[x][y + 1] == EL_PLAYER_IS_LEAVING)
6726       return;
6727
6728     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6729                                          MovDir[x][y + 1] != MV_DOWN ||
6730                                          MovPos[x][y + 1] <= TILEY / 2));
6731
6732     // do not smash moving elements that left the smashed field in time
6733     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6734         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6735       object_hit = FALSE;
6736
6737 #if USE_QUICKSAND_IMPACT_BUGFIX
6738     if (Tile[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6739     {
6740       RemoveMovingField(x, y + 1);
6741       Tile[x][y + 1] = EL_QUICKSAND_EMPTY;
6742       Tile[x][y + 2] = EL_ROCK;
6743       TEST_DrawLevelField(x, y + 2);
6744
6745       object_hit = TRUE;
6746     }
6747
6748     if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6749     {
6750       RemoveMovingField(x, y + 1);
6751       Tile[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6752       Tile[x][y + 2] = EL_ROCK;
6753       TEST_DrawLevelField(x, y + 2);
6754
6755       object_hit = TRUE;
6756     }
6757 #endif
6758
6759     if (object_hit)
6760       smashed = MovingOrBlocked2Element(x, y + 1);
6761
6762     impact = (last_line || object_hit);
6763   }
6764
6765   if (!last_line && smashed == EL_ACID) // element falls into acid
6766   {
6767     SplashAcid(x, y + 1);
6768     return;
6769   }
6770
6771   // !!! not sufficient for all cases -- see EL_PEARL below !!!
6772   // only reset graphic animation if graphic really changes after impact
6773   if (impact &&
6774       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6775   {
6776     ResetGfxAnimation(x, y);
6777     TEST_DrawLevelField(x, y);
6778   }
6779
6780   if (impact && CAN_EXPLODE_IMPACT(element))
6781   {
6782     Bang(x, y);
6783     return;
6784   }
6785   else if (impact && element == EL_PEARL &&
6786            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6787   {
6788     ResetGfxAnimation(x, y);
6789
6790     Tile[x][y] = EL_PEARL_BREAKING;
6791     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6792     return;
6793   }
6794   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6795   {
6796     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6797
6798     return;
6799   }
6800
6801   if (impact && element == EL_AMOEBA_DROP)
6802   {
6803     if (object_hit && IS_PLAYER(x, y + 1))
6804       KillPlayerUnlessEnemyProtected(x, y + 1);
6805     else if (object_hit && smashed == EL_PENGUIN)
6806       Bang(x, y + 1);
6807     else
6808     {
6809       Tile[x][y] = EL_AMOEBA_GROWING;
6810       Store[x][y] = EL_AMOEBA_WET;
6811
6812       ResetRandomAnimationValue(x, y);
6813     }
6814     return;
6815   }
6816
6817   if (object_hit)               // check which object was hit
6818   {
6819     if ((CAN_PASS_MAGIC_WALL(element) && 
6820          (smashed == EL_MAGIC_WALL ||
6821           smashed == EL_BD_MAGIC_WALL)) ||
6822         (CAN_PASS_DC_MAGIC_WALL(element) &&
6823          smashed == EL_DC_MAGIC_WALL))
6824     {
6825       int xx, yy;
6826       int activated_magic_wall =
6827         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6828          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6829          EL_DC_MAGIC_WALL_ACTIVE);
6830
6831       // activate magic wall / mill
6832       SCAN_PLAYFIELD(xx, yy)
6833       {
6834         if (Tile[xx][yy] == smashed)
6835           Tile[xx][yy] = activated_magic_wall;
6836       }
6837
6838       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6839       game.magic_wall_active = TRUE;
6840
6841       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6842                             SND_MAGIC_WALL_ACTIVATING :
6843                             smashed == EL_BD_MAGIC_WALL ?
6844                             SND_BD_MAGIC_WALL_ACTIVATING :
6845                             SND_DC_MAGIC_WALL_ACTIVATING));
6846     }
6847
6848     if (IS_PLAYER(x, y + 1))
6849     {
6850       if (CAN_SMASH_PLAYER(element))
6851       {
6852         KillPlayerUnlessEnemyProtected(x, y + 1);
6853         return;
6854       }
6855     }
6856     else if (smashed == EL_PENGUIN)
6857     {
6858       if (CAN_SMASH_PLAYER(element))
6859       {
6860         Bang(x, y + 1);
6861         return;
6862       }
6863     }
6864     else if (element == EL_BD_DIAMOND)
6865     {
6866       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6867       {
6868         Bang(x, y + 1);
6869         return;
6870       }
6871     }
6872     else if (((element == EL_SP_INFOTRON ||
6873                element == EL_SP_ZONK) &&
6874               (smashed == EL_SP_SNIKSNAK ||
6875                smashed == EL_SP_ELECTRON ||
6876                smashed == EL_SP_DISK_ORANGE)) ||
6877              (element == EL_SP_INFOTRON &&
6878               smashed == EL_SP_DISK_YELLOW))
6879     {
6880       Bang(x, y + 1);
6881       return;
6882     }
6883     else if (CAN_SMASH_EVERYTHING(element))
6884     {
6885       if (IS_CLASSIC_ENEMY(smashed) ||
6886           CAN_EXPLODE_SMASHED(smashed))
6887       {
6888         Bang(x, y + 1);
6889         return;
6890       }
6891       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6892       {
6893         if (smashed == EL_LAMP ||
6894             smashed == EL_LAMP_ACTIVE)
6895         {
6896           Bang(x, y + 1);
6897           return;
6898         }
6899         else if (smashed == EL_NUT)
6900         {
6901           Tile[x][y + 1] = EL_NUT_BREAKING;
6902           PlayLevelSound(x, y, SND_NUT_BREAKING);
6903           RaiseScoreElement(EL_NUT);
6904           return;
6905         }
6906         else if (smashed == EL_PEARL)
6907         {
6908           ResetGfxAnimation(x, y);
6909
6910           Tile[x][y + 1] = EL_PEARL_BREAKING;
6911           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6912           return;
6913         }
6914         else if (smashed == EL_DIAMOND)
6915         {
6916           Tile[x][y + 1] = EL_DIAMOND_BREAKING;
6917           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6918           return;
6919         }
6920         else if (IS_BELT_SWITCH(smashed))
6921         {
6922           ToggleBeltSwitch(x, y + 1);
6923         }
6924         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6925                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6926                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6927                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6928         {
6929           ToggleSwitchgateSwitch();
6930         }
6931         else if (smashed == EL_LIGHT_SWITCH ||
6932                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6933         {
6934           ToggleLightSwitch(x, y + 1);
6935         }
6936         else
6937         {
6938           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6939
6940           CheckElementChangeBySide(x, y + 1, smashed, element,
6941                                    CE_SWITCHED, CH_SIDE_TOP);
6942           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6943                                             CH_SIDE_TOP);
6944         }
6945       }
6946       else
6947       {
6948         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6949       }
6950     }
6951   }
6952
6953   // play sound of magic wall / mill
6954   if (!last_line &&
6955       (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6956        Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6957        Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6958   {
6959     if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6960       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6961     else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6962       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6963     else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6964       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6965
6966     return;
6967   }
6968
6969   // play sound of object that hits the ground
6970   if (last_line || object_hit)
6971     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6972 }
6973
6974 static void TurnRoundExt(int x, int y)
6975 {
6976   static struct
6977   {
6978     int dx, dy;
6979   } move_xy[] =
6980   {
6981     {  0,  0 },
6982     { -1,  0 },
6983     { +1,  0 },
6984     {  0,  0 },
6985     {  0, -1 },
6986     {  0,  0 }, { 0, 0 }, { 0, 0 },
6987     {  0, +1 }
6988   };
6989   static struct
6990   {
6991     int left, right, back;
6992   } turn[] =
6993   {
6994     { 0,        0,              0        },
6995     { MV_DOWN,  MV_UP,          MV_RIGHT },
6996     { MV_UP,    MV_DOWN,        MV_LEFT  },
6997     { 0,        0,              0        },
6998     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6999     { 0,        0,              0        },
7000     { 0,        0,              0        },
7001     { 0,        0,              0        },
7002     { MV_RIGHT, MV_LEFT,        MV_UP    }
7003   };
7004
7005   int element = Tile[x][y];
7006   int move_pattern = element_info[element].move_pattern;
7007
7008   int old_move_dir = MovDir[x][y];
7009   int left_dir  = turn[old_move_dir].left;
7010   int right_dir = turn[old_move_dir].right;
7011   int back_dir  = turn[old_move_dir].back;
7012
7013   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
7014   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
7015   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
7016   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
7017
7018   int left_x  = x + left_dx,  left_y  = y + left_dy;
7019   int right_x = x + right_dx, right_y = y + right_dy;
7020   int move_x  = x + move_dx,  move_y  = y + move_dy;
7021
7022   int xx, yy;
7023
7024   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
7025   {
7026     TestIfBadThingTouchesOtherBadThing(x, y);
7027
7028     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
7029       MovDir[x][y] = right_dir;
7030     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7031       MovDir[x][y] = left_dir;
7032
7033     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
7034       MovDelay[x][y] = 9;
7035     else if (element == EL_BD_BUTTERFLY)     // && MovDir[x][y] == left_dir)
7036       MovDelay[x][y] = 1;
7037   }
7038   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
7039   {
7040     TestIfBadThingTouchesOtherBadThing(x, y);
7041
7042     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
7043       MovDir[x][y] = left_dir;
7044     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7045       MovDir[x][y] = right_dir;
7046
7047     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
7048       MovDelay[x][y] = 9;
7049     else if (element == EL_BD_FIREFLY)      // && MovDir[x][y] == right_dir)
7050       MovDelay[x][y] = 1;
7051   }
7052   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
7053   {
7054     TestIfBadThingTouchesOtherBadThing(x, y);
7055
7056     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
7057       MovDir[x][y] = left_dir;
7058     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
7059       MovDir[x][y] = right_dir;
7060
7061     if (MovDir[x][y] != old_move_dir)
7062       MovDelay[x][y] = 9;
7063   }
7064   else if (element == EL_YAMYAM)
7065   {
7066     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
7067     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
7068
7069     if (can_turn_left && can_turn_right)
7070       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7071     else if (can_turn_left)
7072       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7073     else if (can_turn_right)
7074       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7075     else
7076       MovDir[x][y] = back_dir;
7077
7078     MovDelay[x][y] = 16 + 16 * RND(3);
7079   }
7080   else if (element == EL_DARK_YAMYAM)
7081   {
7082     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7083                                                          left_x, left_y);
7084     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7085                                                          right_x, right_y);
7086
7087     if (can_turn_left && can_turn_right)
7088       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7089     else if (can_turn_left)
7090       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7091     else if (can_turn_right)
7092       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7093     else
7094       MovDir[x][y] = back_dir;
7095
7096     MovDelay[x][y] = 16 + 16 * RND(3);
7097   }
7098   else if (element == EL_PACMAN)
7099   {
7100     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
7101     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
7102
7103     if (can_turn_left && can_turn_right)
7104       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7105     else if (can_turn_left)
7106       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7107     else if (can_turn_right)
7108       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7109     else
7110       MovDir[x][y] = back_dir;
7111
7112     MovDelay[x][y] = 6 + RND(40);
7113   }
7114   else if (element == EL_PIG)
7115   {
7116     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
7117     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
7118     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
7119     boolean should_turn_left, should_turn_right, should_move_on;
7120     int rnd_value = 24;
7121     int rnd = RND(rnd_value);
7122
7123     should_turn_left = (can_turn_left &&
7124                         (!can_move_on ||
7125                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
7126                                                    y + back_dy + left_dy)));
7127     should_turn_right = (can_turn_right &&
7128                          (!can_move_on ||
7129                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
7130                                                     y + back_dy + right_dy)));
7131     should_move_on = (can_move_on &&
7132                       (!can_turn_left ||
7133                        !can_turn_right ||
7134                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
7135                                                  y + move_dy + left_dy) ||
7136                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
7137                                                  y + move_dy + right_dy)));
7138
7139     if (should_turn_left || should_turn_right || should_move_on)
7140     {
7141       if (should_turn_left && should_turn_right && should_move_on)
7142         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
7143                         rnd < 2 * rnd_value / 3 ? right_dir :
7144                         old_move_dir);
7145       else if (should_turn_left && should_turn_right)
7146         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7147       else if (should_turn_left && should_move_on)
7148         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
7149       else if (should_turn_right && should_move_on)
7150         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
7151       else if (should_turn_left)
7152         MovDir[x][y] = left_dir;
7153       else if (should_turn_right)
7154         MovDir[x][y] = right_dir;
7155       else if (should_move_on)
7156         MovDir[x][y] = old_move_dir;
7157     }
7158     else if (can_move_on && rnd > rnd_value / 8)
7159       MovDir[x][y] = old_move_dir;
7160     else if (can_turn_left && can_turn_right)
7161       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7162     else if (can_turn_left && rnd > rnd_value / 8)
7163       MovDir[x][y] = left_dir;
7164     else if (can_turn_right && rnd > rnd_value/8)
7165       MovDir[x][y] = right_dir;
7166     else
7167       MovDir[x][y] = back_dir;
7168
7169     xx = x + move_xy[MovDir[x][y]].dx;
7170     yy = y + move_xy[MovDir[x][y]].dy;
7171
7172     if (!IN_LEV_FIELD(xx, yy) ||
7173         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Tile[xx][yy])))
7174       MovDir[x][y] = old_move_dir;
7175
7176     MovDelay[x][y] = 0;
7177   }
7178   else if (element == EL_DRAGON)
7179   {
7180     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7181     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7182     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7183     int rnd_value = 24;
7184     int rnd = RND(rnd_value);
7185
7186     if (can_move_on && rnd > rnd_value / 8)
7187       MovDir[x][y] = old_move_dir;
7188     else if (can_turn_left && can_turn_right)
7189       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7190     else if (can_turn_left && rnd > rnd_value / 8)
7191       MovDir[x][y] = left_dir;
7192     else if (can_turn_right && rnd > rnd_value / 8)
7193       MovDir[x][y] = right_dir;
7194     else
7195       MovDir[x][y] = back_dir;
7196
7197     xx = x + move_xy[MovDir[x][y]].dx;
7198     yy = y + move_xy[MovDir[x][y]].dy;
7199
7200     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7201       MovDir[x][y] = old_move_dir;
7202
7203     MovDelay[x][y] = 0;
7204   }
7205   else if (element == EL_MOLE)
7206   {
7207     boolean can_move_on =
7208       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7209                             IS_AMOEBOID(Tile[move_x][move_y]) ||
7210                             Tile[move_x][move_y] == EL_AMOEBA_SHRINKING));
7211     if (!can_move_on)
7212     {
7213       boolean can_turn_left =
7214         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7215                               IS_AMOEBOID(Tile[left_x][left_y])));
7216
7217       boolean can_turn_right =
7218         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7219                               IS_AMOEBOID(Tile[right_x][right_y])));
7220
7221       if (can_turn_left && can_turn_right)
7222         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7223       else if (can_turn_left)
7224         MovDir[x][y] = left_dir;
7225       else
7226         MovDir[x][y] = right_dir;
7227     }
7228
7229     if (MovDir[x][y] != old_move_dir)
7230       MovDelay[x][y] = 9;
7231   }
7232   else if (element == EL_BALLOON)
7233   {
7234     MovDir[x][y] = game.wind_direction;
7235     MovDelay[x][y] = 0;
7236   }
7237   else if (element == EL_SPRING)
7238   {
7239     if (MovDir[x][y] & MV_HORIZONTAL)
7240     {
7241       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7242           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7243       {
7244         Tile[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7245         ResetGfxAnimation(move_x, move_y);
7246         TEST_DrawLevelField(move_x, move_y);
7247
7248         MovDir[x][y] = back_dir;
7249       }
7250       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7251                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7252         MovDir[x][y] = MV_NONE;
7253     }
7254
7255     MovDelay[x][y] = 0;
7256   }
7257   else if (element == EL_ROBOT ||
7258            element == EL_SATELLITE ||
7259            element == EL_PENGUIN ||
7260            element == EL_EMC_ANDROID)
7261   {
7262     int attr_x = -1, attr_y = -1;
7263
7264     if (game.all_players_gone)
7265     {
7266       attr_x = game.exit_x;
7267       attr_y = game.exit_y;
7268     }
7269     else
7270     {
7271       int i;
7272
7273       for (i = 0; i < MAX_PLAYERS; i++)
7274       {
7275         struct PlayerInfo *player = &stored_player[i];
7276         int jx = player->jx, jy = player->jy;
7277
7278         if (!player->active)
7279           continue;
7280
7281         if (attr_x == -1 ||
7282             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7283         {
7284           attr_x = jx;
7285           attr_y = jy;
7286         }
7287       }
7288     }
7289
7290     if (element == EL_ROBOT &&
7291         game.robot_wheel_x >= 0 &&
7292         game.robot_wheel_y >= 0 &&
7293         (Tile[game.robot_wheel_x][game.robot_wheel_y] == EL_ROBOT_WHEEL_ACTIVE ||
7294          game.engine_version < VERSION_IDENT(3,1,0,0)))
7295     {
7296       attr_x = game.robot_wheel_x;
7297       attr_y = game.robot_wheel_y;
7298     }
7299
7300     if (element == EL_PENGUIN)
7301     {
7302       int i;
7303       struct XY *xy = xy_topdown;
7304
7305       for (i = 0; i < NUM_DIRECTIONS; i++)
7306       {
7307         int ex = x + xy[i].x;
7308         int ey = y + xy[i].y;
7309
7310         if (IN_LEV_FIELD(ex, ey) && (Tile[ex][ey] == EL_EXIT_OPEN ||
7311                                      Tile[ex][ey] == EL_EM_EXIT_OPEN ||
7312                                      Tile[ex][ey] == EL_STEEL_EXIT_OPEN ||
7313                                      Tile[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7314         {
7315           attr_x = ex;
7316           attr_y = ey;
7317           break;
7318         }
7319       }
7320     }
7321
7322     MovDir[x][y] = MV_NONE;
7323     if (attr_x < x)
7324       MovDir[x][y] |= (game.all_players_gone ? MV_RIGHT : MV_LEFT);
7325     else if (attr_x > x)
7326       MovDir[x][y] |= (game.all_players_gone ? MV_LEFT : MV_RIGHT);
7327     if (attr_y < y)
7328       MovDir[x][y] |= (game.all_players_gone ? MV_DOWN : MV_UP);
7329     else if (attr_y > y)
7330       MovDir[x][y] |= (game.all_players_gone ? MV_UP : MV_DOWN);
7331
7332     if (element == EL_ROBOT)
7333     {
7334       int newx, newy;
7335
7336       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7337         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7338       Moving2Blocked(x, y, &newx, &newy);
7339
7340       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7341         MovDelay[x][y] = 8 + 8 * !RND(3);
7342       else
7343         MovDelay[x][y] = 16;
7344     }
7345     else if (element == EL_PENGUIN)
7346     {
7347       int newx, newy;
7348
7349       MovDelay[x][y] = 1;
7350
7351       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7352       {
7353         boolean first_horiz = RND(2);
7354         int new_move_dir = MovDir[x][y];
7355
7356         MovDir[x][y] =
7357           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7358         Moving2Blocked(x, y, &newx, &newy);
7359
7360         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7361           return;
7362
7363         MovDir[x][y] =
7364           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7365         Moving2Blocked(x, y, &newx, &newy);
7366
7367         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7368           return;
7369
7370         MovDir[x][y] = old_move_dir;
7371         return;
7372       }
7373     }
7374     else if (element == EL_SATELLITE)
7375     {
7376       int newx, newy;
7377
7378       MovDelay[x][y] = 1;
7379
7380       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7381       {
7382         boolean first_horiz = RND(2);
7383         int new_move_dir = MovDir[x][y];
7384
7385         MovDir[x][y] =
7386           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7387         Moving2Blocked(x, y, &newx, &newy);
7388
7389         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7390           return;
7391
7392         MovDir[x][y] =
7393           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7394         Moving2Blocked(x, y, &newx, &newy);
7395
7396         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7397           return;
7398
7399         MovDir[x][y] = old_move_dir;
7400         return;
7401       }
7402     }
7403     else if (element == EL_EMC_ANDROID)
7404     {
7405       static int check_pos[16] =
7406       {
7407         -1,             //  0 => (invalid)
7408         7,              //  1 => MV_LEFT
7409         3,              //  2 => MV_RIGHT
7410         -1,             //  3 => (invalid)
7411         1,              //  4 =>            MV_UP
7412         0,              //  5 => MV_LEFT  | MV_UP
7413         2,              //  6 => MV_RIGHT | MV_UP
7414         -1,             //  7 => (invalid)
7415         5,              //  8 =>            MV_DOWN
7416         6,              //  9 => MV_LEFT  | MV_DOWN
7417         4,              // 10 => MV_RIGHT | MV_DOWN
7418         -1,             // 11 => (invalid)
7419         -1,             // 12 => (invalid)
7420         -1,             // 13 => (invalid)
7421         -1,             // 14 => (invalid)
7422         -1,             // 15 => (invalid)
7423       };
7424       static struct
7425       {
7426         int dx, dy;
7427         int dir;
7428       } check_xy[8] =
7429       {
7430         { -1, -1,       MV_LEFT  | MV_UP   },
7431         {  0, -1,                  MV_UP   },
7432         { +1, -1,       MV_RIGHT | MV_UP   },
7433         { +1,  0,       MV_RIGHT           },
7434         { +1, +1,       MV_RIGHT | MV_DOWN },
7435         {  0, +1,                  MV_DOWN },
7436         { -1, +1,       MV_LEFT  | MV_DOWN },
7437         { -1,  0,       MV_LEFT            },
7438       };
7439       int start_pos, check_order;
7440       boolean can_clone = FALSE;
7441       int i;
7442
7443       // check if there is any free field around current position
7444       for (i = 0; i < 8; i++)
7445       {
7446         int newx = x + check_xy[i].dx;
7447         int newy = y + check_xy[i].dy;
7448
7449         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7450         {
7451           can_clone = TRUE;
7452
7453           break;
7454         }
7455       }
7456
7457       if (can_clone)            // randomly find an element to clone
7458       {
7459         can_clone = FALSE;
7460
7461         start_pos = check_pos[RND(8)];
7462         check_order = (RND(2) ? -1 : +1);
7463
7464         for (i = 0; i < 8; i++)
7465         {
7466           int pos_raw = start_pos + i * check_order;
7467           int pos = (pos_raw + 8) % 8;
7468           int newx = x + check_xy[pos].dx;
7469           int newy = y + check_xy[pos].dy;
7470
7471           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7472           {
7473             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7474             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7475
7476             Store[x][y] = Tile[newx][newy];
7477
7478             can_clone = TRUE;
7479
7480             break;
7481           }
7482         }
7483       }
7484
7485       if (can_clone)            // randomly find a direction to move
7486       {
7487         can_clone = FALSE;
7488
7489         start_pos = check_pos[RND(8)];
7490         check_order = (RND(2) ? -1 : +1);
7491
7492         for (i = 0; i < 8; i++)
7493         {
7494           int pos_raw = start_pos + i * check_order;
7495           int pos = (pos_raw + 8) % 8;
7496           int newx = x + check_xy[pos].dx;
7497           int newy = y + check_xy[pos].dy;
7498           int new_move_dir = check_xy[pos].dir;
7499
7500           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7501           {
7502             MovDir[x][y] = new_move_dir;
7503             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7504
7505             can_clone = TRUE;
7506
7507             break;
7508           }
7509         }
7510       }
7511
7512       if (can_clone)            // cloning and moving successful
7513         return;
7514
7515       // cannot clone -- try to move towards player
7516
7517       start_pos = check_pos[MovDir[x][y] & 0x0f];
7518       check_order = (RND(2) ? -1 : +1);
7519
7520       for (i = 0; i < 3; i++)
7521       {
7522         // first check start_pos, then previous/next or (next/previous) pos
7523         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7524         int pos = (pos_raw + 8) % 8;
7525         int newx = x + check_xy[pos].dx;
7526         int newy = y + check_xy[pos].dy;
7527         int new_move_dir = check_xy[pos].dir;
7528
7529         if (IS_PLAYER(newx, newy))
7530           break;
7531
7532         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7533         {
7534           MovDir[x][y] = new_move_dir;
7535           MovDelay[x][y] = level.android_move_time * 8 + 1;
7536
7537           break;
7538         }
7539       }
7540     }
7541   }
7542   else if (move_pattern == MV_TURNING_LEFT ||
7543            move_pattern == MV_TURNING_RIGHT ||
7544            move_pattern == MV_TURNING_LEFT_RIGHT ||
7545            move_pattern == MV_TURNING_RIGHT_LEFT ||
7546            move_pattern == MV_TURNING_RANDOM ||
7547            move_pattern == MV_ALL_DIRECTIONS)
7548   {
7549     boolean can_turn_left =
7550       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7551     boolean can_turn_right =
7552       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y);
7553
7554     if (element_info[element].move_stepsize == 0)       // "not moving"
7555       return;
7556
7557     if (move_pattern == MV_TURNING_LEFT)
7558       MovDir[x][y] = left_dir;
7559     else if (move_pattern == MV_TURNING_RIGHT)
7560       MovDir[x][y] = right_dir;
7561     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7562       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7563     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7564       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7565     else if (move_pattern == MV_TURNING_RANDOM)
7566       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7567                       can_turn_right && !can_turn_left ? right_dir :
7568                       RND(2) ? left_dir : right_dir);
7569     else if (can_turn_left && can_turn_right)
7570       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7571     else if (can_turn_left)
7572       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7573     else if (can_turn_right)
7574       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7575     else
7576       MovDir[x][y] = back_dir;
7577
7578     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7579   }
7580   else if (move_pattern == MV_HORIZONTAL ||
7581            move_pattern == MV_VERTICAL)
7582   {
7583     if (move_pattern & old_move_dir)
7584       MovDir[x][y] = back_dir;
7585     else if (move_pattern == MV_HORIZONTAL)
7586       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7587     else if (move_pattern == MV_VERTICAL)
7588       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7589
7590     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7591   }
7592   else if (move_pattern & MV_ANY_DIRECTION)
7593   {
7594     MovDir[x][y] = move_pattern;
7595     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7596   }
7597   else if (move_pattern & MV_WIND_DIRECTION)
7598   {
7599     MovDir[x][y] = game.wind_direction;
7600     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7601   }
7602   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7603   {
7604     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7605       MovDir[x][y] = left_dir;
7606     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7607       MovDir[x][y] = right_dir;
7608
7609     if (MovDir[x][y] != old_move_dir)
7610       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7611   }
7612   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7613   {
7614     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7615       MovDir[x][y] = right_dir;
7616     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7617       MovDir[x][y] = left_dir;
7618
7619     if (MovDir[x][y] != old_move_dir)
7620       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7621   }
7622   else if (move_pattern == MV_TOWARDS_PLAYER ||
7623            move_pattern == MV_AWAY_FROM_PLAYER)
7624   {
7625     int attr_x = -1, attr_y = -1;
7626     int newx, newy;
7627     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7628
7629     if (game.all_players_gone)
7630     {
7631       attr_x = game.exit_x;
7632       attr_y = game.exit_y;
7633     }
7634     else
7635     {
7636       int i;
7637
7638       for (i = 0; i < MAX_PLAYERS; i++)
7639       {
7640         struct PlayerInfo *player = &stored_player[i];
7641         int jx = player->jx, jy = player->jy;
7642
7643         if (!player->active)
7644           continue;
7645
7646         if (attr_x == -1 ||
7647             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7648         {
7649           attr_x = jx;
7650           attr_y = jy;
7651         }
7652       }
7653     }
7654
7655     MovDir[x][y] = MV_NONE;
7656     if (attr_x < x)
7657       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7658     else if (attr_x > x)
7659       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7660     if (attr_y < y)
7661       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7662     else if (attr_y > y)
7663       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7664
7665     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7666
7667     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7668     {
7669       boolean first_horiz = RND(2);
7670       int new_move_dir = MovDir[x][y];
7671
7672       if (element_info[element].move_stepsize == 0)     // "not moving"
7673       {
7674         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7675         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7676
7677         return;
7678       }
7679
7680       MovDir[x][y] =
7681         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7682       Moving2Blocked(x, y, &newx, &newy);
7683
7684       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7685         return;
7686
7687       MovDir[x][y] =
7688         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7689       Moving2Blocked(x, y, &newx, &newy);
7690
7691       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7692         return;
7693
7694       MovDir[x][y] = old_move_dir;
7695     }
7696   }
7697   else if (move_pattern == MV_WHEN_PUSHED ||
7698            move_pattern == MV_WHEN_DROPPED)
7699   {
7700     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7701       MovDir[x][y] = MV_NONE;
7702
7703     MovDelay[x][y] = 0;
7704   }
7705   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7706   {
7707     struct XY *test_xy = xy_topdown;
7708     static int test_dir[4] =
7709     {
7710       MV_UP,
7711       MV_LEFT,
7712       MV_RIGHT,
7713       MV_DOWN
7714     };
7715     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7716     int move_preference = -1000000;     // start with very low preference
7717     int new_move_dir = MV_NONE;
7718     int start_test = RND(4);
7719     int i;
7720
7721     for (i = 0; i < NUM_DIRECTIONS; i++)
7722     {
7723       int j = (start_test + i) % 4;
7724       int move_dir = test_dir[j];
7725       int move_dir_preference;
7726
7727       xx = x + test_xy[j].x;
7728       yy = y + test_xy[j].y;
7729
7730       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7731           (IS_PLAYER(xx, yy) || Tile[xx][yy] == EL_PLAYER_IS_LEAVING))
7732       {
7733         new_move_dir = move_dir;
7734
7735         break;
7736       }
7737
7738       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7739         continue;
7740
7741       move_dir_preference = -1 * RunnerVisit[xx][yy];
7742       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7743         move_dir_preference = PlayerVisit[xx][yy];
7744
7745       if (move_dir_preference > move_preference)
7746       {
7747         // prefer field that has not been visited for the longest time
7748         move_preference = move_dir_preference;
7749         new_move_dir = move_dir;
7750       }
7751       else if (move_dir_preference == move_preference &&
7752                move_dir == old_move_dir)
7753       {
7754         // prefer last direction when all directions are preferred equally
7755         move_preference = move_dir_preference;
7756         new_move_dir = move_dir;
7757       }
7758     }
7759
7760     MovDir[x][y] = new_move_dir;
7761     if (old_move_dir != new_move_dir)
7762       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7763   }
7764 }
7765
7766 static void TurnRound(int x, int y)
7767 {
7768   int direction = MovDir[x][y];
7769
7770   TurnRoundExt(x, y);
7771
7772   GfxDir[x][y] = MovDir[x][y];
7773
7774   if (direction != MovDir[x][y])
7775     GfxFrame[x][y] = 0;
7776
7777   if (MovDelay[x][y])
7778     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7779
7780   ResetGfxFrame(x, y);
7781 }
7782
7783 static boolean JustBeingPushed(int x, int y)
7784 {
7785   int i;
7786
7787   for (i = 0; i < MAX_PLAYERS; i++)
7788   {
7789     struct PlayerInfo *player = &stored_player[i];
7790
7791     if (player->active && player->is_pushing && player->MovPos)
7792     {
7793       int next_jx = player->jx + (player->jx - player->last_jx);
7794       int next_jy = player->jy + (player->jy - player->last_jy);
7795
7796       if (x == next_jx && y == next_jy)
7797         return TRUE;
7798     }
7799   }
7800
7801   return FALSE;
7802 }
7803
7804 static void StartMoving(int x, int y)
7805 {
7806   boolean started_moving = FALSE;       // some elements can fall _and_ move
7807   int element = Tile[x][y];
7808
7809   if (Stop[x][y])
7810     return;
7811
7812   if (MovDelay[x][y] == 0)
7813     GfxAction[x][y] = ACTION_DEFAULT;
7814
7815   if (CAN_FALL(element) && y < lev_fieldy - 1)
7816   {
7817     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7818         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7819       if (JustBeingPushed(x, y))
7820         return;
7821
7822     if (element == EL_QUICKSAND_FULL)
7823     {
7824       if (IS_FREE(x, y + 1))
7825       {
7826         InitMovingField(x, y, MV_DOWN);
7827         started_moving = TRUE;
7828
7829         Tile[x][y] = EL_QUICKSAND_EMPTYING;
7830 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7831         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7832           Store[x][y] = EL_ROCK;
7833 #else
7834         Store[x][y] = EL_ROCK;
7835 #endif
7836
7837         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7838       }
7839       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7840       {
7841         if (!MovDelay[x][y])
7842         {
7843           MovDelay[x][y] = TILEY + 1;
7844
7845           ResetGfxAnimation(x, y);
7846           ResetGfxAnimation(x, y + 1);
7847         }
7848
7849         if (MovDelay[x][y])
7850         {
7851           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7852           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7853
7854           MovDelay[x][y]--;
7855           if (MovDelay[x][y])
7856             return;
7857         }
7858
7859         Tile[x][y] = EL_QUICKSAND_EMPTY;
7860         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7861         Store[x][y + 1] = Store[x][y];
7862         Store[x][y] = 0;
7863
7864         PlayLevelSoundAction(x, y, ACTION_FILLING);
7865       }
7866       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7867       {
7868         if (!MovDelay[x][y])
7869         {
7870           MovDelay[x][y] = TILEY + 1;
7871
7872           ResetGfxAnimation(x, y);
7873           ResetGfxAnimation(x, y + 1);
7874         }
7875
7876         if (MovDelay[x][y])
7877         {
7878           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7879           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7880
7881           MovDelay[x][y]--;
7882           if (MovDelay[x][y])
7883             return;
7884         }
7885
7886         Tile[x][y] = EL_QUICKSAND_EMPTY;
7887         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7888         Store[x][y + 1] = Store[x][y];
7889         Store[x][y] = 0;
7890
7891         PlayLevelSoundAction(x, y, ACTION_FILLING);
7892       }
7893     }
7894     else if (element == EL_QUICKSAND_FAST_FULL)
7895     {
7896       if (IS_FREE(x, y + 1))
7897       {
7898         InitMovingField(x, y, MV_DOWN);
7899         started_moving = TRUE;
7900
7901         Tile[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7902 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7903         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7904           Store[x][y] = EL_ROCK;
7905 #else
7906         Store[x][y] = EL_ROCK;
7907 #endif
7908
7909         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7910       }
7911       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7912       {
7913         if (!MovDelay[x][y])
7914         {
7915           MovDelay[x][y] = TILEY + 1;
7916
7917           ResetGfxAnimation(x, y);
7918           ResetGfxAnimation(x, y + 1);
7919         }
7920
7921         if (MovDelay[x][y])
7922         {
7923           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7924           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7925
7926           MovDelay[x][y]--;
7927           if (MovDelay[x][y])
7928             return;
7929         }
7930
7931         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7932         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7933         Store[x][y + 1] = Store[x][y];
7934         Store[x][y] = 0;
7935
7936         PlayLevelSoundAction(x, y, ACTION_FILLING);
7937       }
7938       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7939       {
7940         if (!MovDelay[x][y])
7941         {
7942           MovDelay[x][y] = TILEY + 1;
7943
7944           ResetGfxAnimation(x, y);
7945           ResetGfxAnimation(x, y + 1);
7946         }
7947
7948         if (MovDelay[x][y])
7949         {
7950           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7951           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7952
7953           MovDelay[x][y]--;
7954           if (MovDelay[x][y])
7955             return;
7956         }
7957
7958         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7959         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7960         Store[x][y + 1] = Store[x][y];
7961         Store[x][y] = 0;
7962
7963         PlayLevelSoundAction(x, y, ACTION_FILLING);
7964       }
7965     }
7966     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7967              Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7968     {
7969       InitMovingField(x, y, MV_DOWN);
7970       started_moving = TRUE;
7971
7972       Tile[x][y] = EL_QUICKSAND_FILLING;
7973       Store[x][y] = element;
7974
7975       PlayLevelSoundAction(x, y, ACTION_FILLING);
7976     }
7977     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7978              Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7979     {
7980       InitMovingField(x, y, MV_DOWN);
7981       started_moving = TRUE;
7982
7983       Tile[x][y] = EL_QUICKSAND_FAST_FILLING;
7984       Store[x][y] = element;
7985
7986       PlayLevelSoundAction(x, y, ACTION_FILLING);
7987     }
7988     else if (element == EL_MAGIC_WALL_FULL)
7989     {
7990       if (IS_FREE(x, y + 1))
7991       {
7992         InitMovingField(x, y, MV_DOWN);
7993         started_moving = TRUE;
7994
7995         Tile[x][y] = EL_MAGIC_WALL_EMPTYING;
7996         Store[x][y] = EL_CHANGED(Store[x][y]);
7997       }
7998       else if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7999       {
8000         if (!MovDelay[x][y])
8001           MovDelay[x][y] = TILEY / 4 + 1;
8002
8003         if (MovDelay[x][y])
8004         {
8005           MovDelay[x][y]--;
8006           if (MovDelay[x][y])
8007             return;
8008         }
8009
8010         Tile[x][y] = EL_MAGIC_WALL_ACTIVE;
8011         Tile[x][y + 1] = EL_MAGIC_WALL_FULL;
8012         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
8013         Store[x][y] = 0;
8014       }
8015     }
8016     else if (element == EL_BD_MAGIC_WALL_FULL)
8017     {
8018       if (IS_FREE(x, y + 1))
8019       {
8020         InitMovingField(x, y, MV_DOWN);
8021         started_moving = TRUE;
8022
8023         Tile[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
8024         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
8025       }
8026       else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
8027       {
8028         if (!MovDelay[x][y])
8029           MovDelay[x][y] = TILEY / 4 + 1;
8030
8031         if (MovDelay[x][y])
8032         {
8033           MovDelay[x][y]--;
8034           if (MovDelay[x][y])
8035             return;
8036         }
8037
8038         Tile[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
8039         Tile[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
8040         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
8041         Store[x][y] = 0;
8042       }
8043     }
8044     else if (element == EL_DC_MAGIC_WALL_FULL)
8045     {
8046       if (IS_FREE(x, y + 1))
8047       {
8048         InitMovingField(x, y, MV_DOWN);
8049         started_moving = TRUE;
8050
8051         Tile[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
8052         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
8053       }
8054       else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
8055       {
8056         if (!MovDelay[x][y])
8057           MovDelay[x][y] = TILEY / 4 + 1;
8058
8059         if (MovDelay[x][y])
8060         {
8061           MovDelay[x][y]--;
8062           if (MovDelay[x][y])
8063             return;
8064         }
8065
8066         Tile[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
8067         Tile[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
8068         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
8069         Store[x][y] = 0;
8070       }
8071     }
8072     else if ((CAN_PASS_MAGIC_WALL(element) &&
8073               (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
8074                Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
8075              (CAN_PASS_DC_MAGIC_WALL(element) &&
8076               (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
8077
8078     {
8079       InitMovingField(x, y, MV_DOWN);
8080       started_moving = TRUE;
8081
8082       Tile[x][y] =
8083         (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
8084          Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
8085          EL_DC_MAGIC_WALL_FILLING);
8086       Store[x][y] = element;
8087     }
8088     else if (CAN_FALL(element) && Tile[x][y + 1] == EL_ACID)
8089     {
8090       SplashAcid(x, y + 1);
8091
8092       InitMovingField(x, y, MV_DOWN);
8093       started_moving = TRUE;
8094
8095       Store[x][y] = EL_ACID;
8096     }
8097     else if (
8098              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8099               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
8100              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
8101               CAN_FALL(element) && WasJustFalling[x][y] &&
8102               (Tile[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
8103
8104              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
8105               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
8106               (Tile[x][y + 1] == EL_BLOCKED)))
8107     {
8108       /* this is needed for a special case not covered by calling "Impact()"
8109          from "ContinueMoving()": if an element moves to a tile directly below
8110          another element which was just falling on that tile (which was empty
8111          in the previous frame), the falling element above would just stop
8112          instead of smashing the element below (in previous version, the above
8113          element was just checked for "moving" instead of "falling", resulting
8114          in incorrect smashes caused by horizontal movement of the above
8115          element; also, the case of the player being the element to smash was
8116          simply not covered here... :-/ ) */
8117
8118       CheckCollision[x][y] = 0;
8119       CheckImpact[x][y] = 0;
8120
8121       Impact(x, y);
8122     }
8123     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
8124     {
8125       if (MovDir[x][y] == MV_NONE)
8126       {
8127         InitMovingField(x, y, MV_DOWN);
8128         started_moving = TRUE;
8129       }
8130     }
8131     else if (IS_FREE(x, y + 1) || Tile[x][y + 1] == EL_DIAMOND_BREAKING)
8132     {
8133       if (WasJustFalling[x][y]) // prevent animation from being restarted
8134         MovDir[x][y] = MV_DOWN;
8135
8136       InitMovingField(x, y, MV_DOWN);
8137       started_moving = TRUE;
8138     }
8139     else if (element == EL_AMOEBA_DROP)
8140     {
8141       Tile[x][y] = EL_AMOEBA_GROWING;
8142       Store[x][y] = EL_AMOEBA_WET;
8143     }
8144     else if (((IS_SLIPPERY(Tile[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8145               (IS_EM_SLIPPERY_WALL(Tile[x][y + 1]) && IS_GEM(element))) &&
8146              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8147              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8148     {
8149       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
8150                                 (IS_FREE(x - 1, y + 1) ||
8151                                  Tile[x - 1][y + 1] == EL_ACID));
8152       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8153                                 (IS_FREE(x + 1, y + 1) ||
8154                                  Tile[x + 1][y + 1] == EL_ACID));
8155       boolean can_fall_any  = (can_fall_left || can_fall_right);
8156       boolean can_fall_both = (can_fall_left && can_fall_right);
8157       int slippery_type = element_info[Tile[x][y + 1]].slippery_type;
8158
8159       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8160       {
8161         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8162           can_fall_right = FALSE;
8163         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8164           can_fall_left = FALSE;
8165         else if (slippery_type == SLIPPERY_ONLY_LEFT)
8166           can_fall_right = FALSE;
8167         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8168           can_fall_left = FALSE;
8169
8170         can_fall_any  = (can_fall_left || can_fall_right);
8171         can_fall_both = FALSE;
8172       }
8173
8174       if (can_fall_both)
8175       {
8176         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8177           can_fall_right = FALSE;       // slip down on left side
8178         else
8179           can_fall_left = !(can_fall_right = RND(2));
8180
8181         can_fall_both = FALSE;
8182       }
8183
8184       if (can_fall_any)
8185       {
8186         // if not determined otherwise, prefer left side for slipping down
8187         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8188         started_moving = TRUE;
8189       }
8190     }
8191     else if (IS_BELT_ACTIVE(Tile[x][y + 1]))
8192     {
8193       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
8194       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8195       int belt_nr = getBeltNrFromBeltActiveElement(Tile[x][y + 1]);
8196       int belt_dir = game.belt_dir[belt_nr];
8197
8198       if ((belt_dir == MV_LEFT  && left_is_free) ||
8199           (belt_dir == MV_RIGHT && right_is_free))
8200       {
8201         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8202
8203         InitMovingField(x, y, belt_dir);
8204         started_moving = TRUE;
8205
8206         Pushed[x][y] = TRUE;
8207         Pushed[nextx][y] = TRUE;
8208
8209         GfxAction[x][y] = ACTION_DEFAULT;
8210       }
8211       else
8212       {
8213         MovDir[x][y] = 0;       // if element was moving, stop it
8214       }
8215     }
8216   }
8217
8218   // not "else if" because of elements that can fall and move (EL_SPRING)
8219   if (CAN_MOVE(element) && !started_moving)
8220   {
8221     int move_pattern = element_info[element].move_pattern;
8222     int newx, newy;
8223
8224     Moving2Blocked(x, y, &newx, &newy);
8225
8226     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8227       return;
8228
8229     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8230         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8231     {
8232       WasJustMoving[x][y] = 0;
8233       CheckCollision[x][y] = 0;
8234
8235       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8236
8237       if (Tile[x][y] != element)        // element has changed
8238         return;
8239     }
8240
8241     if (!MovDelay[x][y])        // start new movement phase
8242     {
8243       // all objects that can change their move direction after each step
8244       // (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall
8245
8246       if (element != EL_YAMYAM &&
8247           element != EL_DARK_YAMYAM &&
8248           element != EL_PACMAN &&
8249           !(move_pattern & MV_ANY_DIRECTION) &&
8250           move_pattern != MV_TURNING_LEFT &&
8251           move_pattern != MV_TURNING_RIGHT &&
8252           move_pattern != MV_TURNING_LEFT_RIGHT &&
8253           move_pattern != MV_TURNING_RIGHT_LEFT &&
8254           move_pattern != MV_TURNING_RANDOM)
8255       {
8256         TurnRound(x, y);
8257
8258         if (MovDelay[x][y] && (element == EL_BUG ||
8259                                element == EL_SPACESHIP ||
8260                                element == EL_SP_SNIKSNAK ||
8261                                element == EL_SP_ELECTRON ||
8262                                element == EL_MOLE))
8263           TEST_DrawLevelField(x, y);
8264       }
8265     }
8266
8267     if (MovDelay[x][y])         // wait some time before next movement
8268     {
8269       MovDelay[x][y]--;
8270
8271       if (element == EL_ROBOT ||
8272           element == EL_YAMYAM ||
8273           element == EL_DARK_YAMYAM)
8274       {
8275         DrawLevelElementAnimationIfNeeded(x, y, element);
8276         PlayLevelSoundAction(x, y, ACTION_WAITING);
8277       }
8278       else if (element == EL_SP_ELECTRON)
8279         DrawLevelElementAnimationIfNeeded(x, y, element);
8280       else if (element == EL_DRAGON)
8281       {
8282         int i;
8283         int dir = MovDir[x][y];
8284         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8285         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8286         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8287                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8288                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8289                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8290         int frame = getGraphicAnimationFrameXY(graphic, x, y);
8291
8292         GfxAction[x][y] = ACTION_ATTACKING;
8293
8294         if (IS_PLAYER(x, y))
8295           DrawPlayerField(x, y);
8296         else
8297           TEST_DrawLevelField(x, y);
8298
8299         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8300
8301         for (i = 1; i <= 3; i++)
8302         {
8303           int xx = x + i * dx;
8304           int yy = y + i * dy;
8305           int sx = SCREENX(xx);
8306           int sy = SCREENY(yy);
8307           int flame_graphic = graphic + (i - 1);
8308
8309           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Tile[xx][yy]))
8310             break;
8311
8312           if (MovDelay[x][y])
8313           {
8314             int flamed = MovingOrBlocked2Element(xx, yy);
8315
8316             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8317               Bang(xx, yy);
8318             else
8319               RemoveMovingField(xx, yy);
8320
8321             ChangeDelay[xx][yy] = 0;
8322
8323             Tile[xx][yy] = EL_FLAMES;
8324
8325             if (IN_SCR_FIELD(sx, sy))
8326             {
8327               TEST_DrawLevelFieldCrumbled(xx, yy);
8328               DrawScreenGraphic(sx, sy, flame_graphic, frame);
8329             }
8330           }
8331           else
8332           {
8333             if (Tile[xx][yy] == EL_FLAMES)
8334               Tile[xx][yy] = EL_EMPTY;
8335             TEST_DrawLevelField(xx, yy);
8336           }
8337         }
8338       }
8339
8340       if (MovDelay[x][y])       // element still has to wait some time
8341       {
8342         PlayLevelSoundAction(x, y, ACTION_WAITING);
8343
8344         return;
8345       }
8346     }
8347
8348     // now make next step
8349
8350     Moving2Blocked(x, y, &newx, &newy); // get next screen position
8351
8352     if (DONT_COLLIDE_WITH(element) &&
8353         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8354         !PLAYER_ENEMY_PROTECTED(newx, newy))
8355     {
8356       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8357
8358       return;
8359     }
8360
8361     else if (CAN_MOVE_INTO_ACID(element) &&
8362              IN_LEV_FIELD(newx, newy) && Tile[newx][newy] == EL_ACID &&
8363              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8364              (MovDir[x][y] == MV_DOWN ||
8365               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8366     {
8367       SplashAcid(newx, newy);
8368       Store[x][y] = EL_ACID;
8369     }
8370     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8371     {
8372       if (Tile[newx][newy] == EL_EXIT_OPEN ||
8373           Tile[newx][newy] == EL_EM_EXIT_OPEN ||
8374           Tile[newx][newy] == EL_STEEL_EXIT_OPEN ||
8375           Tile[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8376       {
8377         RemoveField(x, y);
8378         TEST_DrawLevelField(x, y);
8379
8380         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8381         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8382           DrawGraphicThruMask(SCREENX(newx), SCREENY(newy), el2img(element), 0);
8383
8384         game.friends_still_needed--;
8385         if (!game.friends_still_needed &&
8386             !game.GameOver &&
8387             game.all_players_gone)
8388           LevelSolved();
8389
8390         return;
8391       }
8392       else if (IS_FOOD_PENGUIN(Tile[newx][newy]))
8393       {
8394         if (DigField(local_player, x, y, newx, newy, 0, 0, DF_DIG) == MP_MOVING)
8395           TEST_DrawLevelField(newx, newy);
8396         else
8397           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8398       }
8399       else if (!IS_FREE(newx, newy))
8400       {
8401         GfxAction[x][y] = ACTION_WAITING;
8402
8403         if (IS_PLAYER(x, y))
8404           DrawPlayerField(x, y);
8405         else
8406           TEST_DrawLevelField(x, y);
8407
8408         return;
8409       }
8410     }
8411     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8412     {
8413       if (IS_FOOD_PIG(Tile[newx][newy]))
8414       {
8415         if (IS_MOVING(newx, newy))
8416           RemoveMovingField(newx, newy);
8417         else
8418         {
8419           Tile[newx][newy] = EL_EMPTY;
8420           TEST_DrawLevelField(newx, newy);
8421         }
8422
8423         PlayLevelSound(x, y, SND_PIG_DIGGING);
8424       }
8425       else if (!IS_FREE(newx, newy))
8426       {
8427         if (IS_PLAYER(x, y))
8428           DrawPlayerField(x, y);
8429         else
8430           TEST_DrawLevelField(x, y);
8431
8432         return;
8433       }
8434     }
8435     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8436     {
8437       if (Store[x][y] != EL_EMPTY)
8438       {
8439         boolean can_clone = FALSE;
8440         int xx, yy;
8441
8442         // check if element to clone is still there
8443         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8444         {
8445           if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == Store[x][y])
8446           {
8447             can_clone = TRUE;
8448
8449             break;
8450           }
8451         }
8452
8453         // cannot clone or target field not free anymore -- do not clone
8454         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8455           Store[x][y] = EL_EMPTY;
8456       }
8457
8458       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8459       {
8460         if (IS_MV_DIAGONAL(MovDir[x][y]))
8461         {
8462           int diagonal_move_dir = MovDir[x][y];
8463           int stored = Store[x][y];
8464           int change_delay = 8;
8465           int graphic;
8466
8467           // android is moving diagonally
8468
8469           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8470
8471           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8472           GfxElement[x][y] = EL_EMC_ANDROID;
8473           GfxAction[x][y] = ACTION_SHRINKING;
8474           GfxDir[x][y] = diagonal_move_dir;
8475           ChangeDelay[x][y] = change_delay;
8476
8477           if (Store[x][y] == EL_EMPTY)
8478             Store[x][y] = GfxElementEmpty[x][y];
8479
8480           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8481                                    GfxDir[x][y]);
8482
8483           DrawLevelGraphicAnimation(x, y, graphic);
8484           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8485
8486           if (Tile[newx][newy] == EL_ACID)
8487           {
8488             SplashAcid(newx, newy);
8489
8490             return;
8491           }
8492
8493           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8494
8495           Store[newx][newy] = EL_EMC_ANDROID;
8496           GfxElement[newx][newy] = EL_EMC_ANDROID;
8497           GfxAction[newx][newy] = ACTION_GROWING;
8498           GfxDir[newx][newy] = diagonal_move_dir;
8499           ChangeDelay[newx][newy] = change_delay;
8500
8501           graphic = el_act_dir2img(GfxElement[newx][newy],
8502                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8503
8504           DrawLevelGraphicAnimation(newx, newy, graphic);
8505           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8506
8507           return;
8508         }
8509         else
8510         {
8511           Tile[newx][newy] = EL_EMPTY;
8512           TEST_DrawLevelField(newx, newy);
8513
8514           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8515         }
8516       }
8517       else if (!IS_FREE(newx, newy))
8518       {
8519         return;
8520       }
8521     }
8522     else if (IS_CUSTOM_ELEMENT(element) &&
8523              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8524     {
8525       if (!DigFieldByCE(newx, newy, element))
8526         return;
8527
8528       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8529       {
8530         RunnerVisit[x][y] = FrameCounter;
8531         PlayerVisit[x][y] /= 8;         // expire player visit path
8532       }
8533     }
8534     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8535     {
8536       if (!IS_FREE(newx, newy))
8537       {
8538         if (IS_PLAYER(x, y))
8539           DrawPlayerField(x, y);
8540         else
8541           TEST_DrawLevelField(x, y);
8542
8543         return;
8544       }
8545       else
8546       {
8547         boolean wanna_flame = !RND(10);
8548         int dx = newx - x, dy = newy - y;
8549         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8550         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8551         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8552                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8553         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8554                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8555
8556         if ((wanna_flame ||
8557              IS_CLASSIC_ENEMY(element1) ||
8558              IS_CLASSIC_ENEMY(element2)) &&
8559             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8560             element1 != EL_FLAMES && element2 != EL_FLAMES)
8561         {
8562           ResetGfxAnimation(x, y);
8563           GfxAction[x][y] = ACTION_ATTACKING;
8564
8565           if (IS_PLAYER(x, y))
8566             DrawPlayerField(x, y);
8567           else
8568             TEST_DrawLevelField(x, y);
8569
8570           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8571
8572           MovDelay[x][y] = 50;
8573
8574           Tile[newx][newy] = EL_FLAMES;
8575           if (IN_LEV_FIELD(newx1, newy1) && Tile[newx1][newy1] == EL_EMPTY)
8576             Tile[newx1][newy1] = EL_FLAMES;
8577           if (IN_LEV_FIELD(newx2, newy2) && Tile[newx2][newy2] == EL_EMPTY)
8578             Tile[newx2][newy2] = EL_FLAMES;
8579
8580           return;
8581         }
8582       }
8583     }
8584     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8585              Tile[newx][newy] == EL_DIAMOND)
8586     {
8587       if (IS_MOVING(newx, newy))
8588         RemoveMovingField(newx, newy);
8589       else
8590       {
8591         Tile[newx][newy] = EL_EMPTY;
8592         TEST_DrawLevelField(newx, newy);
8593       }
8594
8595       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8596     }
8597     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8598              IS_FOOD_DARK_YAMYAM(Tile[newx][newy]))
8599     {
8600       if (AmoebaNr[newx][newy])
8601       {
8602         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8603         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8604             Tile[newx][newy] == EL_BD_AMOEBA)
8605           AmoebaCnt[AmoebaNr[newx][newy]]--;
8606       }
8607
8608       if (IS_MOVING(newx, newy))
8609       {
8610         RemoveMovingField(newx, newy);
8611       }
8612       else
8613       {
8614         Tile[newx][newy] = EL_EMPTY;
8615         TEST_DrawLevelField(newx, newy);
8616       }
8617
8618       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8619     }
8620     else if ((element == EL_PACMAN || element == EL_MOLE)
8621              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Tile[newx][newy]))
8622     {
8623       if (AmoebaNr[newx][newy])
8624       {
8625         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8626         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8627             Tile[newx][newy] == EL_BD_AMOEBA)
8628           AmoebaCnt[AmoebaNr[newx][newy]]--;
8629       }
8630
8631       if (element == EL_MOLE)
8632       {
8633         Tile[newx][newy] = EL_AMOEBA_SHRINKING;
8634         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8635
8636         ResetGfxAnimation(x, y);
8637         GfxAction[x][y] = ACTION_DIGGING;
8638         TEST_DrawLevelField(x, y);
8639
8640         MovDelay[newx][newy] = 0;       // start amoeba shrinking delay
8641
8642         return;                         // wait for shrinking amoeba
8643       }
8644       else      // element == EL_PACMAN
8645       {
8646         Tile[newx][newy] = EL_EMPTY;
8647         TEST_DrawLevelField(newx, newy);
8648         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8649       }
8650     }
8651     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8652              (Tile[newx][newy] == EL_AMOEBA_SHRINKING ||
8653               (Tile[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8654     {
8655       // wait for shrinking amoeba to completely disappear
8656       return;
8657     }
8658     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8659     {
8660       // object was running against a wall
8661
8662       TurnRound(x, y);
8663
8664       if (GFX_ELEMENT(element) != EL_SAND)     // !!! FIX THIS (crumble) !!!
8665         DrawLevelElementAnimation(x, y, element);
8666
8667       if (DONT_TOUCH(element))
8668         TestIfBadThingTouchesPlayer(x, y);
8669
8670       return;
8671     }
8672
8673     InitMovingField(x, y, MovDir[x][y]);
8674
8675     PlayLevelSoundAction(x, y, ACTION_MOVING);
8676   }
8677
8678   if (MovDir[x][y])
8679     ContinueMoving(x, y);
8680 }
8681
8682 void ContinueMoving(int x, int y)
8683 {
8684   int element = Tile[x][y];
8685   struct ElementInfo *ei = &element_info[element];
8686   int direction = MovDir[x][y];
8687   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8688   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8689   int newx = x + dx, newy = y + dy;
8690   int stored = Store[x][y];
8691   int stored_new = Store[newx][newy];
8692   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8693   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8694   boolean last_line = (newy == lev_fieldy - 1);
8695   boolean use_step_delay = (GET_MAX_STEP_DELAY(element) != 0);
8696
8697   if (pushed_by_player)         // special case: moving object pushed by player
8698   {
8699     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x, y)->MovPos));
8700   }
8701   else if (use_step_delay)      // special case: moving object has step delay
8702   {
8703     if (!MovDelay[x][y])
8704       MovPos[x][y] += getElementMoveStepsize(x, y);
8705
8706     if (MovDelay[x][y])
8707       MovDelay[x][y]--;
8708     else
8709       MovDelay[x][y] = GET_NEW_STEP_DELAY(element);
8710
8711     if (MovDelay[x][y])
8712     {
8713       TEST_DrawLevelField(x, y);
8714
8715       return;   // element is still waiting
8716     }
8717   }
8718   else                          // normal case: generically moving object
8719   {
8720     MovPos[x][y] += getElementMoveStepsize(x, y);
8721   }
8722
8723   if (ABS(MovPos[x][y]) < TILEX)
8724   {
8725     TEST_DrawLevelField(x, y);
8726
8727     return;     // element is still moving
8728   }
8729
8730   // element reached destination field
8731
8732   Tile[x][y] = EL_EMPTY;
8733   Tile[newx][newy] = element;
8734   MovPos[x][y] = 0;     // force "not moving" for "crumbled sand"
8735
8736   if (Store[x][y] == EL_ACID)   // element is moving into acid pool
8737   {
8738     element = Tile[newx][newy] = EL_ACID;
8739   }
8740   else if (element == EL_MOLE)
8741   {
8742     Tile[x][y] = EL_SAND;
8743
8744     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8745   }
8746   else if (element == EL_QUICKSAND_FILLING)
8747   {
8748     element = Tile[newx][newy] = get_next_element(element);
8749     Store[newx][newy] = Store[x][y];
8750   }
8751   else if (element == EL_QUICKSAND_EMPTYING)
8752   {
8753     Tile[x][y] = get_next_element(element);
8754     element = Tile[newx][newy] = Store[x][y];
8755   }
8756   else if (element == EL_QUICKSAND_FAST_FILLING)
8757   {
8758     element = Tile[newx][newy] = get_next_element(element);
8759     Store[newx][newy] = Store[x][y];
8760   }
8761   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8762   {
8763     Tile[x][y] = get_next_element(element);
8764     element = Tile[newx][newy] = Store[x][y];
8765   }
8766   else if (element == EL_MAGIC_WALL_FILLING)
8767   {
8768     element = Tile[newx][newy] = get_next_element(element);
8769     if (!game.magic_wall_active)
8770       element = Tile[newx][newy] = EL_MAGIC_WALL_DEAD;
8771     Store[newx][newy] = Store[x][y];
8772   }
8773   else if (element == EL_MAGIC_WALL_EMPTYING)
8774   {
8775     Tile[x][y] = get_next_element(element);
8776     if (!game.magic_wall_active)
8777       Tile[x][y] = EL_MAGIC_WALL_DEAD;
8778     element = Tile[newx][newy] = Store[x][y];
8779
8780     InitField(newx, newy, FALSE);
8781   }
8782   else if (element == EL_BD_MAGIC_WALL_FILLING)
8783   {
8784     element = Tile[newx][newy] = get_next_element(element);
8785     if (!game.magic_wall_active)
8786       element = Tile[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8787     Store[newx][newy] = Store[x][y];
8788   }
8789   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8790   {
8791     Tile[x][y] = get_next_element(element);
8792     if (!game.magic_wall_active)
8793       Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
8794     element = Tile[newx][newy] = Store[x][y];
8795
8796     InitField(newx, newy, FALSE);
8797   }
8798   else if (element == EL_DC_MAGIC_WALL_FILLING)
8799   {
8800     element = Tile[newx][newy] = get_next_element(element);
8801     if (!game.magic_wall_active)
8802       element = Tile[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8803     Store[newx][newy] = Store[x][y];
8804   }
8805   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8806   {
8807     Tile[x][y] = get_next_element(element);
8808     if (!game.magic_wall_active)
8809       Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
8810     element = Tile[newx][newy] = Store[x][y];
8811
8812     InitField(newx, newy, FALSE);
8813   }
8814   else if (element == EL_AMOEBA_DROPPING)
8815   {
8816     Tile[x][y] = get_next_element(element);
8817     element = Tile[newx][newy] = Store[x][y];
8818   }
8819   else if (element == EL_SOKOBAN_OBJECT)
8820   {
8821     if (Back[x][y])
8822       Tile[x][y] = Back[x][y];
8823
8824     if (Back[newx][newy])
8825       Tile[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8826
8827     Back[x][y] = Back[newx][newy] = 0;
8828   }
8829
8830   Store[x][y] = EL_EMPTY;
8831   MovPos[x][y] = 0;
8832   MovDir[x][y] = 0;
8833   MovDelay[x][y] = 0;
8834
8835   MovDelay[newx][newy] = 0;
8836
8837   if (CAN_CHANGE_OR_HAS_ACTION(element))
8838   {
8839     // copy element change control values to new field
8840     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8841     ChangePage[newx][newy]  = ChangePage[x][y];
8842     ChangeCount[newx][newy] = ChangeCount[x][y];
8843     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8844   }
8845
8846   CustomValue[newx][newy] = CustomValue[x][y];
8847
8848   ChangeDelay[x][y] = 0;
8849   ChangePage[x][y] = -1;
8850   ChangeCount[x][y] = 0;
8851   ChangeEvent[x][y] = -1;
8852
8853   CustomValue[x][y] = 0;
8854
8855   // copy animation control values to new field
8856   GfxFrame[newx][newy]  = GfxFrame[x][y];
8857   GfxRandom[newx][newy] = GfxRandom[x][y];      // keep same random value
8858   GfxAction[newx][newy] = GfxAction[x][y];      // keep action one frame
8859   GfxDir[newx][newy]    = GfxDir[x][y];         // keep element direction
8860
8861   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8862
8863   // some elements can leave other elements behind after moving
8864   if (ei->move_leave_element != EL_EMPTY &&
8865       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8866       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8867   {
8868     int move_leave_element = ei->move_leave_element;
8869
8870     // this makes it possible to leave the removed element again
8871     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8872       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8873
8874     Tile[x][y] = move_leave_element;
8875
8876     if (element_info[Tile[x][y]].move_direction_initial == MV_START_PREVIOUS)
8877       MovDir[x][y] = direction;
8878
8879     InitField(x, y, FALSE);
8880
8881     if (GFX_CRUMBLED(Tile[x][y]))
8882       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8883
8884     if (IS_PLAYER_ELEMENT(move_leave_element))
8885       RelocatePlayer(x, y, move_leave_element);
8886   }
8887
8888   // do this after checking for left-behind element
8889   ResetGfxAnimation(x, y);      // reset animation values for old field
8890
8891   if (!CAN_MOVE(element) ||
8892       (CAN_FALL(element) && direction == MV_DOWN &&
8893        (element == EL_SPRING ||
8894         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8895         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8896     GfxDir[x][y] = MovDir[newx][newy] = 0;
8897
8898   TEST_DrawLevelField(x, y);
8899   TEST_DrawLevelField(newx, newy);
8900
8901   Stop[newx][newy] = TRUE;      // ignore this element until the next frame
8902
8903   // prevent pushed element from moving on in pushed direction
8904   if (pushed_by_player && CAN_MOVE(element) &&
8905       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8906       !(element_info[element].move_pattern & direction))
8907     TurnRound(newx, newy);
8908
8909   // prevent elements on conveyor belt from moving on in last direction
8910   if (pushed_by_conveyor && CAN_FALL(element) &&
8911       direction & MV_HORIZONTAL)
8912     MovDir[newx][newy] = 0;
8913
8914   if (!pushed_by_player)
8915   {
8916     int nextx = newx + dx, nexty = newy + dy;
8917     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8918
8919     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8920
8921     if (CAN_FALL(element) && direction == MV_DOWN)
8922       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8923
8924     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8925       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8926
8927     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8928       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8929   }
8930
8931   if (DONT_TOUCH(element))      // object may be nasty to player or others
8932   {
8933     TestIfBadThingTouchesPlayer(newx, newy);
8934     TestIfBadThingTouchesFriend(newx, newy);
8935
8936     if (!IS_CUSTOM_ELEMENT(element))
8937       TestIfBadThingTouchesOtherBadThing(newx, newy);
8938   }
8939   else if (element == EL_PENGUIN)
8940     TestIfFriendTouchesBadThing(newx, newy);
8941
8942   if (DONT_GET_HIT_BY(element))
8943   {
8944     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8945   }
8946
8947   // give the player one last chance (one more frame) to move away
8948   if (CAN_FALL(element) && direction == MV_DOWN &&
8949       (last_line || (!IS_FREE(x, newy + 1) &&
8950                      (!IS_PLAYER(x, newy + 1) ||
8951                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8952     Impact(x, newy);
8953
8954   if (pushed_by_player && !game.use_change_when_pushing_bug)
8955   {
8956     int push_side = MV_DIR_OPPOSITE(direction);
8957     struct PlayerInfo *player = PLAYERINFO(x, y);
8958
8959     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8960                                player->index_bit, push_side);
8961     CheckTriggeredElementChangeByPlayer(newx, newy, element, CE_PLAYER_PUSHES_X,
8962                                         player->index_bit, push_side);
8963   }
8964
8965   if (element == EL_EMC_ANDROID && pushed_by_player)    // make another move
8966     MovDelay[newx][newy] = 1;
8967
8968   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8969
8970   TestIfElementTouchesCustomElement(x, y);      // empty or new element
8971   TestIfElementHitsCustomElement(newx, newy, direction);
8972   TestIfPlayerTouchesCustomElement(newx, newy);
8973   TestIfElementTouchesCustomElement(newx, newy);
8974
8975   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8976       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8977     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8978                              MV_DIR_OPPOSITE(direction));
8979 }
8980
8981 int AmoebaNeighbourNr(int ax, int ay)
8982 {
8983   int i;
8984   int element = Tile[ax][ay];
8985   int group_nr = 0;
8986   struct XY *xy = xy_topdown;
8987
8988   for (i = 0; i < NUM_DIRECTIONS; i++)
8989   {
8990     int x = ax + xy[i].x;
8991     int y = ay + xy[i].y;
8992
8993     if (!IN_LEV_FIELD(x, y))
8994       continue;
8995
8996     if (Tile[x][y] == element && AmoebaNr[x][y] > 0)
8997       group_nr = AmoebaNr[x][y];
8998   }
8999
9000   return group_nr;
9001 }
9002
9003 static void AmoebaMerge(int ax, int ay)
9004 {
9005   int i, x, y, xx, yy;
9006   int new_group_nr = AmoebaNr[ax][ay];
9007   struct XY *xy = xy_topdown;
9008
9009   if (new_group_nr == 0)
9010     return;
9011
9012   for (i = 0; i < NUM_DIRECTIONS; i++)
9013   {
9014     x = ax + xy[i].x;
9015     y = ay + xy[i].y;
9016
9017     if (!IN_LEV_FIELD(x, y))
9018       continue;
9019
9020     if ((Tile[x][y] == EL_AMOEBA_FULL ||
9021          Tile[x][y] == EL_BD_AMOEBA ||
9022          Tile[x][y] == EL_AMOEBA_DEAD) &&
9023         AmoebaNr[x][y] != new_group_nr)
9024     {
9025       int old_group_nr = AmoebaNr[x][y];
9026
9027       if (old_group_nr == 0)
9028         return;
9029
9030       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
9031       AmoebaCnt[old_group_nr] = 0;
9032       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
9033       AmoebaCnt2[old_group_nr] = 0;
9034
9035       SCAN_PLAYFIELD(xx, yy)
9036       {
9037         if (AmoebaNr[xx][yy] == old_group_nr)
9038           AmoebaNr[xx][yy] = new_group_nr;
9039       }
9040     }
9041   }
9042 }
9043
9044 void AmoebaToDiamond(int ax, int ay)
9045 {
9046   int i, x, y;
9047
9048   if (Tile[ax][ay] == EL_AMOEBA_DEAD)
9049   {
9050     int group_nr = AmoebaNr[ax][ay];
9051
9052 #ifdef DEBUG
9053     if (group_nr == 0)
9054     {
9055       Debug("game:playing:AmoebaToDiamond", "ax = %d, ay = %d", ax, ay);
9056       Debug("game:playing:AmoebaToDiamond", "This should never happen!");
9057
9058       return;
9059     }
9060 #endif
9061
9062     SCAN_PLAYFIELD(x, y)
9063     {
9064       if (Tile[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9065       {
9066         AmoebaNr[x][y] = 0;
9067         Tile[x][y] = EL_AMOEBA_TO_DIAMOND;
9068       }
9069     }
9070
9071     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9072                             SND_AMOEBA_TURNING_TO_GEM :
9073                             SND_AMOEBA_TURNING_TO_ROCK));
9074     Bang(ax, ay);
9075   }
9076   else
9077   {
9078     struct XY *xy = xy_topdown;
9079
9080     for (i = 0; i < NUM_DIRECTIONS; i++)
9081     {
9082       x = ax + xy[i].x;
9083       y = ay + xy[i].y;
9084
9085       if (!IN_LEV_FIELD(x, y))
9086         continue;
9087
9088       if (Tile[x][y] == EL_AMOEBA_TO_DIAMOND)
9089       {
9090         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9091                               SND_AMOEBA_TURNING_TO_GEM :
9092                               SND_AMOEBA_TURNING_TO_ROCK));
9093         Bang(x, y);
9094       }
9095     }
9096   }
9097 }
9098
9099 static void AmoebaToDiamondBD(int ax, int ay, int new_element)
9100 {
9101   int x, y;
9102   int group_nr = AmoebaNr[ax][ay];
9103   boolean done = FALSE;
9104
9105 #ifdef DEBUG
9106   if (group_nr == 0)
9107   {
9108     Debug("game:playing:AmoebaToDiamondBD", "ax = %d, ay = %d", ax, ay);
9109     Debug("game:playing:AmoebaToDiamondBD", "This should never happen!");
9110
9111     return;
9112   }
9113 #endif
9114
9115   SCAN_PLAYFIELD(x, y)
9116   {
9117     if (AmoebaNr[x][y] == group_nr &&
9118         (Tile[x][y] == EL_AMOEBA_DEAD ||
9119          Tile[x][y] == EL_BD_AMOEBA ||
9120          Tile[x][y] == EL_AMOEBA_GROWING))
9121     {
9122       AmoebaNr[x][y] = 0;
9123       Tile[x][y] = new_element;
9124       InitField(x, y, FALSE);
9125       TEST_DrawLevelField(x, y);
9126       done = TRUE;
9127     }
9128   }
9129
9130   if (done)
9131     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9132                             SND_BD_AMOEBA_TURNING_TO_ROCK :
9133                             SND_BD_AMOEBA_TURNING_TO_GEM));
9134 }
9135
9136 static void AmoebaGrowing(int x, int y)
9137 {
9138   static DelayCounter sound_delay = { 0 };
9139
9140   if (!MovDelay[x][y])          // start new growing cycle
9141   {
9142     MovDelay[x][y] = 7;
9143
9144     if (DelayReached(&sound_delay))
9145     {
9146       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9147       sound_delay.value = 30;
9148     }
9149   }
9150
9151   if (MovDelay[x][y])           // wait some time before growing bigger
9152   {
9153     MovDelay[x][y]--;
9154     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9155     {
9156       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9157                                            6 - MovDelay[x][y]);
9158
9159       DrawLevelGraphic(x, y, IMG_AMOEBA_GROWING, frame);
9160     }
9161
9162     if (!MovDelay[x][y])
9163     {
9164       Tile[x][y] = Store[x][y];
9165       Store[x][y] = 0;
9166       TEST_DrawLevelField(x, y);
9167     }
9168   }
9169 }
9170
9171 static void AmoebaShrinking(int x, int y)
9172 {
9173   static DelayCounter sound_delay = { 0 };
9174
9175   if (!MovDelay[x][y])          // start new shrinking cycle
9176   {
9177     MovDelay[x][y] = 7;
9178
9179     if (DelayReached(&sound_delay))
9180       sound_delay.value = 30;
9181   }
9182
9183   if (MovDelay[x][y])           // wait some time before shrinking
9184   {
9185     MovDelay[x][y]--;
9186     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9187     {
9188       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9189                                            6 - MovDelay[x][y]);
9190
9191       DrawLevelGraphic(x, y, IMG_AMOEBA_SHRINKING, frame);
9192     }
9193
9194     if (!MovDelay[x][y])
9195     {
9196       Tile[x][y] = EL_EMPTY;
9197       TEST_DrawLevelField(x, y);
9198
9199       // don't let mole enter this field in this cycle;
9200       // (give priority to objects falling to this field from above)
9201       Stop[x][y] = TRUE;
9202     }
9203   }
9204 }
9205
9206 static void AmoebaReproduce(int ax, int ay)
9207 {
9208   int i;
9209   int element = Tile[ax][ay];
9210   int graphic = el2img(element);
9211   int newax = ax, neway = ay;
9212   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9213   struct XY *xy = xy_topdown;
9214
9215   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9216   {
9217     Tile[ax][ay] = EL_AMOEBA_DEAD;
9218     TEST_DrawLevelField(ax, ay);
9219     return;
9220   }
9221
9222   if (IS_ANIMATED(graphic))
9223     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9224
9225   if (!MovDelay[ax][ay])        // start making new amoeba field
9226     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9227
9228   if (MovDelay[ax][ay])         // wait some time before making new amoeba
9229   {
9230     MovDelay[ax][ay]--;
9231     if (MovDelay[ax][ay])
9232       return;
9233   }
9234
9235   if (can_drop)                 // EL_AMOEBA_WET or EL_EMC_DRIPPER
9236   {
9237     int start = RND(4);
9238     int x = ax + xy[start].x;
9239     int y = ay + xy[start].y;
9240
9241     if (!IN_LEV_FIELD(x, y))
9242       return;
9243
9244     if (IS_FREE(x, y) ||
9245         CAN_GROW_INTO(Tile[x][y]) ||
9246         Tile[x][y] == EL_QUICKSAND_EMPTY ||
9247         Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9248     {
9249       newax = x;
9250       neway = y;
9251     }
9252
9253     if (newax == ax && neway == ay)
9254       return;
9255   }
9256   else                          // normal or "filled" (BD style) amoeba
9257   {
9258     int start = RND(4);
9259     boolean waiting_for_player = FALSE;
9260
9261     for (i = 0; i < NUM_DIRECTIONS; i++)
9262     {
9263       int j = (start + i) % 4;
9264       int x = ax + xy[j].x;
9265       int y = ay + xy[j].y;
9266
9267       if (!IN_LEV_FIELD(x, y))
9268         continue;
9269
9270       if (IS_FREE(x, y) ||
9271           CAN_GROW_INTO(Tile[x][y]) ||
9272           Tile[x][y] == EL_QUICKSAND_EMPTY ||
9273           Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9274       {
9275         newax = x;
9276         neway = y;
9277         break;
9278       }
9279       else if (IS_PLAYER(x, y))
9280         waiting_for_player = TRUE;
9281     }
9282
9283     if (newax == ax && neway == ay)             // amoeba cannot grow
9284     {
9285       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9286       {
9287         Tile[ax][ay] = EL_AMOEBA_DEAD;
9288         TEST_DrawLevelField(ax, ay);
9289         AmoebaCnt[AmoebaNr[ax][ay]]--;
9290
9291         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   // amoeba is completely dead
9292         {
9293           if (element == EL_AMOEBA_FULL)
9294             AmoebaToDiamond(ax, ay);
9295           else if (element == EL_BD_AMOEBA)
9296             AmoebaToDiamondBD(ax, ay, level.amoeba_content);
9297         }
9298       }
9299       return;
9300     }
9301     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9302     {
9303       // amoeba gets larger by growing in some direction
9304
9305       int new_group_nr = AmoebaNr[ax][ay];
9306
9307 #ifdef DEBUG
9308   if (new_group_nr == 0)
9309   {
9310     Debug("game:playing:AmoebaReproduce", "newax = %d, neway = %d",
9311           newax, neway);
9312     Debug("game:playing:AmoebaReproduce", "This should never happen!");
9313
9314     return;
9315   }
9316 #endif
9317
9318       AmoebaNr[newax][neway] = new_group_nr;
9319       AmoebaCnt[new_group_nr]++;
9320       AmoebaCnt2[new_group_nr]++;
9321
9322       // if amoeba touches other amoeba(s) after growing, unify them
9323       AmoebaMerge(newax, neway);
9324
9325       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9326       {
9327         AmoebaToDiamondBD(newax, neway, EL_BD_ROCK);
9328         return;
9329       }
9330     }
9331   }
9332
9333   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9334       (neway == lev_fieldy - 1 && newax != ax))
9335   {
9336     Tile[newax][neway] = EL_AMOEBA_GROWING;     // creation of new amoeba
9337     Store[newax][neway] = element;
9338   }
9339   else if (neway == ay || element == EL_EMC_DRIPPER)
9340   {
9341     Tile[newax][neway] = EL_AMOEBA_DROP;        // drop left/right of amoeba
9342
9343     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9344   }
9345   else
9346   {
9347     InitMovingField(ax, ay, MV_DOWN);           // drop dripping from amoeba
9348     Tile[ax][ay] = EL_AMOEBA_DROPPING;
9349     Store[ax][ay] = EL_AMOEBA_DROP;
9350     ContinueMoving(ax, ay);
9351     return;
9352   }
9353
9354   TEST_DrawLevelField(newax, neway);
9355 }
9356
9357 static void Life(int ax, int ay)
9358 {
9359   int x1, y1, x2, y2;
9360   int life_time = 40;
9361   int element = Tile[ax][ay];
9362   int graphic = el2img(element);
9363   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9364                          level.biomaze);
9365   boolean changed = FALSE;
9366
9367   if (IS_ANIMATED(graphic))
9368     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9369
9370   if (Stop[ax][ay])
9371     return;
9372
9373   if (!MovDelay[ax][ay])        // start new "game of life" cycle
9374     MovDelay[ax][ay] = life_time;
9375
9376   if (MovDelay[ax][ay])         // wait some time before next cycle
9377   {
9378     MovDelay[ax][ay]--;
9379     if (MovDelay[ax][ay])
9380       return;
9381   }
9382
9383   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9384   {
9385     int xx = ax+x1, yy = ay+y1;
9386     int old_element = Tile[xx][yy];
9387     int num_neighbours = 0;
9388
9389     if (!IN_LEV_FIELD(xx, yy))
9390       continue;
9391
9392     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9393     {
9394       int x = xx+x2, y = yy+y2;
9395
9396       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9397         continue;
9398
9399       boolean is_player_cell = (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y));
9400       boolean is_neighbour = FALSE;
9401
9402       if (level.use_life_bugs)
9403         is_neighbour =
9404           (((Tile[x][y] == element || is_player_cell) && !Stop[x][y]) ||
9405            (IS_FREE(x, y)                             &&  Stop[x][y]));
9406       else
9407         is_neighbour =
9408           (Last[x][y] == element || is_player_cell);
9409
9410       if (is_neighbour)
9411         num_neighbours++;
9412     }
9413
9414     boolean is_free = FALSE;
9415
9416     if (level.use_life_bugs)
9417       is_free = (IS_FREE(xx, yy));
9418     else
9419       is_free = (IS_FREE(xx, yy) && Last[xx][yy] == EL_EMPTY);
9420
9421     if (xx == ax && yy == ay)           // field in the middle
9422     {
9423       if (num_neighbours < life_parameter[0] ||
9424           num_neighbours > life_parameter[1])
9425       {
9426         Tile[xx][yy] = EL_EMPTY;
9427         if (Tile[xx][yy] != old_element)
9428           TEST_DrawLevelField(xx, yy);
9429         Stop[xx][yy] = TRUE;
9430         changed = TRUE;
9431       }
9432     }
9433     else if (is_free || CAN_GROW_INTO(Tile[xx][yy]))
9434     {                                   // free border field
9435       if (num_neighbours >= life_parameter[2] &&
9436           num_neighbours <= life_parameter[3])
9437       {
9438         Tile[xx][yy] = element;
9439         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time - 1);
9440         if (Tile[xx][yy] != old_element)
9441           TEST_DrawLevelField(xx, yy);
9442         Stop[xx][yy] = TRUE;
9443         changed = TRUE;
9444       }
9445     }
9446   }
9447
9448   if (changed)
9449     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9450                    SND_GAME_OF_LIFE_GROWING);
9451 }
9452
9453 static void InitRobotWheel(int x, int y)
9454 {
9455   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9456 }
9457
9458 static void RunRobotWheel(int x, int y)
9459 {
9460   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9461 }
9462
9463 static void StopRobotWheel(int x, int y)
9464 {
9465   if (game.robot_wheel_x == x &&
9466       game.robot_wheel_y == y)
9467   {
9468     game.robot_wheel_x = -1;
9469     game.robot_wheel_y = -1;
9470     game.robot_wheel_active = FALSE;
9471   }
9472 }
9473
9474 static void InitTimegateWheel(int x, int y)
9475 {
9476   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9477 }
9478
9479 static void RunTimegateWheel(int x, int y)
9480 {
9481   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9482 }
9483
9484 static void InitMagicBallDelay(int x, int y)
9485 {
9486   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9487 }
9488
9489 static void ActivateMagicBall(int bx, int by)
9490 {
9491   int x, y;
9492
9493   if (level.ball_random)
9494   {
9495     int pos_border = RND(8);    // select one of the eight border elements
9496     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9497     int xx = pos_content % 3;
9498     int yy = pos_content / 3;
9499
9500     x = bx - 1 + xx;
9501     y = by - 1 + yy;
9502
9503     if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9504       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9505   }
9506   else
9507   {
9508     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9509     {
9510       int xx = x - bx + 1;
9511       int yy = y - by + 1;
9512
9513       if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9514         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9515     }
9516   }
9517
9518   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9519 }
9520
9521 static void CheckExit(int x, int y)
9522 {
9523   if (game.gems_still_needed > 0 ||
9524       game.sokoban_fields_still_needed > 0 ||
9525       game.sokoban_objects_still_needed > 0 ||
9526       game.lights_still_needed > 0)
9527   {
9528     int element = Tile[x][y];
9529     int graphic = el2img(element);
9530
9531     if (IS_ANIMATED(graphic))
9532       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9533
9534     return;
9535   }
9536
9537   // do not re-open exit door closed after last player
9538   if (game.all_players_gone)
9539     return;
9540
9541   Tile[x][y] = EL_EXIT_OPENING;
9542
9543   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9544 }
9545
9546 static void CheckExitEM(int x, int y)
9547 {
9548   if (game.gems_still_needed > 0 ||
9549       game.sokoban_fields_still_needed > 0 ||
9550       game.sokoban_objects_still_needed > 0 ||
9551       game.lights_still_needed > 0)
9552   {
9553     int element = Tile[x][y];
9554     int graphic = el2img(element);
9555
9556     if (IS_ANIMATED(graphic))
9557       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9558
9559     return;
9560   }
9561
9562   // do not re-open exit door closed after last player
9563   if (game.all_players_gone)
9564     return;
9565
9566   Tile[x][y] = EL_EM_EXIT_OPENING;
9567
9568   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9569 }
9570
9571 static void CheckExitSteel(int x, int y)
9572 {
9573   if (game.gems_still_needed > 0 ||
9574       game.sokoban_fields_still_needed > 0 ||
9575       game.sokoban_objects_still_needed > 0 ||
9576       game.lights_still_needed > 0)
9577   {
9578     int element = Tile[x][y];
9579     int graphic = el2img(element);
9580
9581     if (IS_ANIMATED(graphic))
9582       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9583
9584     return;
9585   }
9586
9587   // do not re-open exit door closed after last player
9588   if (game.all_players_gone)
9589     return;
9590
9591   Tile[x][y] = EL_STEEL_EXIT_OPENING;
9592
9593   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9594 }
9595
9596 static void CheckExitSteelEM(int x, int y)
9597 {
9598   if (game.gems_still_needed > 0 ||
9599       game.sokoban_fields_still_needed > 0 ||
9600       game.sokoban_objects_still_needed > 0 ||
9601       game.lights_still_needed > 0)
9602   {
9603     int element = Tile[x][y];
9604     int graphic = el2img(element);
9605
9606     if (IS_ANIMATED(graphic))
9607       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9608
9609     return;
9610   }
9611
9612   // do not re-open exit door closed after last player
9613   if (game.all_players_gone)
9614     return;
9615
9616   Tile[x][y] = EL_EM_STEEL_EXIT_OPENING;
9617
9618   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9619 }
9620
9621 static void CheckExitSP(int x, int y)
9622 {
9623   if (game.gems_still_needed > 0)
9624   {
9625     int element = Tile[x][y];
9626     int graphic = el2img(element);
9627
9628     if (IS_ANIMATED(graphic))
9629       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9630
9631     return;
9632   }
9633
9634   // do not re-open exit door closed after last player
9635   if (game.all_players_gone)
9636     return;
9637
9638   Tile[x][y] = EL_SP_EXIT_OPENING;
9639
9640   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9641 }
9642
9643 static void CloseAllOpenTimegates(void)
9644 {
9645   int x, y;
9646
9647   SCAN_PLAYFIELD(x, y)
9648   {
9649     int element = Tile[x][y];
9650
9651     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9652     {
9653       Tile[x][y] = EL_TIMEGATE_CLOSING;
9654
9655       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9656     }
9657   }
9658 }
9659
9660 static void DrawTwinkleOnField(int x, int y)
9661 {
9662   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9663     return;
9664
9665   if (Tile[x][y] == EL_BD_DIAMOND)
9666     return;
9667
9668   if (MovDelay[x][y] == 0)      // next animation frame
9669     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9670
9671   if (MovDelay[x][y] != 0)      // wait some time before next frame
9672   {
9673     MovDelay[x][y]--;
9674
9675     DrawLevelElementAnimation(x, y, Tile[x][y]);
9676
9677     if (MovDelay[x][y] != 0)
9678     {
9679       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9680                                            10 - MovDelay[x][y]);
9681
9682       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9683     }
9684   }
9685 }
9686
9687 static void WallGrowing(int x, int y)
9688 {
9689   int delay = 6;
9690
9691   if (!MovDelay[x][y])          // next animation frame
9692     MovDelay[x][y] = 3 * delay;
9693
9694   if (MovDelay[x][y])           // wait some time before next frame
9695   {
9696     MovDelay[x][y]--;
9697
9698     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9699     {
9700       int graphic = el_dir2img(Tile[x][y], GfxDir[x][y]);
9701       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9702
9703       DrawLevelGraphic(x, y, graphic, frame);
9704     }
9705
9706     if (!MovDelay[x][y])
9707     {
9708       if (MovDir[x][y] == MV_LEFT)
9709       {
9710         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Tile[x - 1][y]))
9711           TEST_DrawLevelField(x - 1, y);
9712       }
9713       else if (MovDir[x][y] == MV_RIGHT)
9714       {
9715         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Tile[x + 1][y]))
9716           TEST_DrawLevelField(x + 1, y);
9717       }
9718       else if (MovDir[x][y] == MV_UP)
9719       {
9720         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Tile[x][y - 1]))
9721           TEST_DrawLevelField(x, y - 1);
9722       }
9723       else
9724       {
9725         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Tile[x][y + 1]))
9726           TEST_DrawLevelField(x, y + 1);
9727       }
9728
9729       Tile[x][y] = Store[x][y];
9730       Store[x][y] = 0;
9731       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9732       TEST_DrawLevelField(x, y);
9733     }
9734   }
9735 }
9736
9737 static void CheckWallGrowing(int ax, int ay)
9738 {
9739   int element = Tile[ax][ay];
9740   int graphic = el2img(element);
9741   boolean free_top    = FALSE;
9742   boolean free_bottom = FALSE;
9743   boolean free_left   = FALSE;
9744   boolean free_right  = FALSE;
9745   boolean stop_top    = FALSE;
9746   boolean stop_bottom = FALSE;
9747   boolean stop_left   = FALSE;
9748   boolean stop_right  = FALSE;
9749   boolean new_wall    = FALSE;
9750
9751   boolean is_steelwall  = (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9752                            element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9753                            element == EL_EXPANDABLE_STEELWALL_ANY);
9754
9755   boolean grow_vertical   = (element == EL_EXPANDABLE_WALL_VERTICAL ||
9756                              element == EL_EXPANDABLE_WALL_ANY ||
9757                              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9758                              element == EL_EXPANDABLE_STEELWALL_ANY);
9759
9760   boolean grow_horizontal = (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9761                              element == EL_EXPANDABLE_WALL_ANY ||
9762                              element == EL_EXPANDABLE_WALL ||
9763                              element == EL_BD_EXPANDABLE_WALL ||
9764                              element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9765                              element == EL_EXPANDABLE_STEELWALL_ANY);
9766
9767   boolean stop_vertical   = (element == EL_EXPANDABLE_WALL_VERTICAL ||
9768                              element == EL_EXPANDABLE_STEELWALL_VERTICAL);
9769
9770   boolean stop_horizontal = (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9771                              element == EL_EXPANDABLE_WALL ||
9772                              element == EL_EXPANDABLE_STEELWALL_HORIZONTAL);
9773
9774   int wall_growing = (is_steelwall ?
9775                       EL_EXPANDABLE_STEELWALL_GROWING :
9776                       EL_EXPANDABLE_WALL_GROWING);
9777
9778   int gfx_wall_growing_up    = (is_steelwall ?
9779                                 IMG_EXPANDABLE_STEELWALL_GROWING_UP :
9780                                 IMG_EXPANDABLE_WALL_GROWING_UP);
9781   int gfx_wall_growing_down  = (is_steelwall ?
9782                                 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN :
9783                                 IMG_EXPANDABLE_WALL_GROWING_DOWN);
9784   int gfx_wall_growing_left  = (is_steelwall ?
9785                                 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT :
9786                                 IMG_EXPANDABLE_WALL_GROWING_LEFT);
9787   int gfx_wall_growing_right = (is_steelwall ?
9788                                 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT :
9789                                 IMG_EXPANDABLE_WALL_GROWING_RIGHT);
9790
9791   if (IS_ANIMATED(graphic))
9792     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9793
9794   if (!MovDelay[ax][ay])        // start building new wall
9795     MovDelay[ax][ay] = 6;
9796
9797   if (MovDelay[ax][ay])         // wait some time before building new wall
9798   {
9799     MovDelay[ax][ay]--;
9800     if (MovDelay[ax][ay])
9801       return;
9802   }
9803
9804   if (IN_LEV_FIELD(ax, ay - 1) && IS_FREE(ax, ay - 1))
9805     free_top = TRUE;
9806   if (IN_LEV_FIELD(ax, ay + 1) && IS_FREE(ax, ay + 1))
9807     free_bottom = TRUE;
9808   if (IN_LEV_FIELD(ax - 1, ay) && IS_FREE(ax - 1, ay))
9809     free_left = TRUE;
9810   if (IN_LEV_FIELD(ax + 1, ay) && IS_FREE(ax + 1, ay))
9811     free_right = TRUE;
9812
9813   if (grow_vertical)
9814   {
9815     if (free_top)
9816     {
9817       Tile[ax][ay - 1] = wall_growing;
9818       Store[ax][ay - 1] = element;
9819       GfxDir[ax][ay - 1] = MovDir[ax][ay - 1] = MV_UP;
9820
9821       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay - 1)))
9822         DrawLevelGraphic(ax, ay - 1, gfx_wall_growing_up, 0);
9823
9824       new_wall = TRUE;
9825     }
9826
9827     if (free_bottom)
9828     {
9829       Tile[ax][ay + 1] = wall_growing;
9830       Store[ax][ay + 1] = element;
9831       GfxDir[ax][ay + 1] = MovDir[ax][ay + 1] = MV_DOWN;
9832
9833       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay + 1)))
9834         DrawLevelGraphic(ax, ay + 1, gfx_wall_growing_down, 0);
9835
9836       new_wall = TRUE;
9837     }
9838   }
9839
9840   if (grow_horizontal)
9841   {
9842     if (free_left)
9843     {
9844       Tile[ax - 1][ay] = wall_growing;
9845       Store[ax - 1][ay] = element;
9846       GfxDir[ax - 1][ay] = MovDir[ax - 1][ay] = MV_LEFT;
9847
9848       if (IN_SCR_FIELD(SCREENX(ax - 1), SCREENY(ay)))
9849         DrawLevelGraphic(ax - 1, ay, gfx_wall_growing_left, 0);
9850
9851       new_wall = TRUE;
9852     }
9853
9854     if (free_right)
9855     {
9856       Tile[ax + 1][ay] = wall_growing;
9857       Store[ax + 1][ay] = element;
9858       GfxDir[ax + 1][ay] = MovDir[ax + 1][ay] = MV_RIGHT;
9859
9860       if (IN_SCR_FIELD(SCREENX(ax + 1), SCREENY(ay)))
9861         DrawLevelGraphic(ax + 1, ay, gfx_wall_growing_right, 0);
9862
9863       new_wall = TRUE;
9864     }
9865   }
9866
9867   if (element == EL_EXPANDABLE_WALL && (free_left || free_right))
9868     TEST_DrawLevelField(ax, ay);
9869
9870   if (!IN_LEV_FIELD(ax, ay - 1) || IS_WALL(Tile[ax][ay - 1]))
9871     stop_top = TRUE;
9872   if (!IN_LEV_FIELD(ax, ay + 1) || IS_WALL(Tile[ax][ay + 1]))
9873     stop_bottom = TRUE;
9874   if (!IN_LEV_FIELD(ax - 1, ay) || IS_WALL(Tile[ax - 1][ay]))
9875     stop_left = TRUE;
9876   if (!IN_LEV_FIELD(ax + 1, ay) || IS_WALL(Tile[ax + 1][ay]))
9877     stop_right = TRUE;
9878
9879   if (((stop_top && stop_bottom) || stop_horizontal) &&
9880       ((stop_left && stop_right) || stop_vertical))
9881     Tile[ax][ay] = (is_steelwall ? EL_STEELWALL : EL_WALL);
9882
9883   if (new_wall)
9884     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9885 }
9886
9887 static void CheckForDragon(int x, int y)
9888 {
9889   int i, j;
9890   boolean dragon_found = FALSE;
9891   struct XY *xy = xy_topdown;
9892
9893   for (i = 0; i < NUM_DIRECTIONS; i++)
9894   {
9895     for (j = 0; j < 4; j++)
9896     {
9897       int xx = x + j * xy[i].x;
9898       int yy = y + j * xy[i].y;
9899
9900       if (IN_LEV_FIELD(xx, yy) &&
9901           (Tile[xx][yy] == EL_FLAMES || Tile[xx][yy] == EL_DRAGON))
9902       {
9903         if (Tile[xx][yy] == EL_DRAGON)
9904           dragon_found = TRUE;
9905       }
9906       else
9907         break;
9908     }
9909   }
9910
9911   if (!dragon_found)
9912   {
9913     for (i = 0; i < NUM_DIRECTIONS; i++)
9914     {
9915       for (j = 0; j < 3; j++)
9916       {
9917         int xx = x + j * xy[i].x;
9918         int yy = y + j * xy[i].y;
9919
9920         if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == EL_FLAMES)
9921         {
9922           Tile[xx][yy] = EL_EMPTY;
9923           TEST_DrawLevelField(xx, yy);
9924         }
9925         else
9926           break;
9927       }
9928     }
9929   }
9930 }
9931
9932 static void InitBuggyBase(int x, int y)
9933 {
9934   int element = Tile[x][y];
9935   int activating_delay = FRAMES_PER_SECOND / 4;
9936
9937   ChangeDelay[x][y] =
9938     (element == EL_SP_BUGGY_BASE ?
9939      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9940      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9941      activating_delay :
9942      element == EL_SP_BUGGY_BASE_ACTIVE ?
9943      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9944 }
9945
9946 static void WarnBuggyBase(int x, int y)
9947 {
9948   int i;
9949   struct XY *xy = xy_topdown;
9950
9951   for (i = 0; i < NUM_DIRECTIONS; i++)
9952   {
9953     int xx = x + xy[i].x;
9954     int yy = y + xy[i].y;
9955
9956     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9957     {
9958       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9959
9960       break;
9961     }
9962   }
9963 }
9964
9965 static void InitTrap(int x, int y)
9966 {
9967   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9968 }
9969
9970 static void ActivateTrap(int x, int y)
9971 {
9972   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9973 }
9974
9975 static void ChangeActiveTrap(int x, int y)
9976 {
9977   int graphic = IMG_TRAP_ACTIVE;
9978
9979   // if new animation frame was drawn, correct crumbled sand border
9980   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9981     TEST_DrawLevelFieldCrumbled(x, y);
9982 }
9983
9984 static int getSpecialActionElement(int element, int number, int base_element)
9985 {
9986   return (element != EL_EMPTY ? element :
9987           number != -1 ? base_element + number - 1 :
9988           EL_EMPTY);
9989 }
9990
9991 static int getModifiedActionNumber(int value_old, int operator, int operand,
9992                                    int value_min, int value_max)
9993 {
9994   int value_new = (operator == CA_MODE_SET      ? operand :
9995                    operator == CA_MODE_ADD      ? value_old + operand :
9996                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9997                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9998                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9999                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
10000                    value_old);
10001
10002   return (value_new < value_min ? value_min :
10003           value_new > value_max ? value_max :
10004           value_new);
10005 }
10006
10007 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10008 {
10009   struct ElementInfo *ei = &element_info[element];
10010   struct ElementChangeInfo *change = &ei->change_page[page];
10011   int target_element = change->target_element;
10012   int action_type = change->action_type;
10013   int action_mode = change->action_mode;
10014   int action_arg = change->action_arg;
10015   int action_element = change->action_element;
10016   int i;
10017
10018   if (!change->has_action)
10019     return;
10020
10021   // ---------- determine action paramater values -----------------------------
10022
10023   int level_time_value =
10024     (level.time > 0 ? TimeLeft :
10025      TimePlayed);
10026
10027   int action_arg_element_raw =
10028     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
10029      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10030      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
10031      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
10032      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10033      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
10034      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
10035      EL_EMPTY);
10036   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10037
10038   int action_arg_direction =
10039     (action_arg >= CA_ARG_DIRECTION_LEFT &&
10040      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10041      action_arg == CA_ARG_DIRECTION_TRIGGER ?
10042      change->actual_trigger_side :
10043      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10044      MV_DIR_OPPOSITE(change->actual_trigger_side) :
10045      MV_NONE);
10046
10047   int action_arg_number_min =
10048     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10049      CA_ARG_MIN);
10050
10051   int action_arg_number_max =
10052     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10053      action_type == CA_SET_LEVEL_GEMS ? 999 :
10054      action_type == CA_SET_LEVEL_TIME ? 9999 :
10055      action_type == CA_SET_LEVEL_SCORE ? 99999 :
10056      action_type == CA_SET_CE_VALUE ? 9999 :
10057      action_type == CA_SET_CE_SCORE ? 9999 :
10058      CA_ARG_MAX);
10059
10060   int action_arg_number_reset =
10061     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10062      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10063      action_type == CA_SET_LEVEL_TIME ? level.time :
10064      action_type == CA_SET_LEVEL_SCORE ? 0 :
10065      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10066      action_type == CA_SET_CE_SCORE ? 0 :
10067      0);
10068
10069   int action_arg_number =
10070     (action_arg <= CA_ARG_MAX ? action_arg :
10071      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10072      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10073      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10074      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10075      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10076      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10077      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10078      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10079      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10080      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10081      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? game.gems_still_needed :
10082      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? game.score :
10083      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10084      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10085      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10086      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10087      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10088      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10089      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
10090      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10091      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
10092      -1);
10093
10094   int action_arg_number_old =
10095     (action_type == CA_SET_LEVEL_GEMS ? game.gems_still_needed :
10096      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10097      action_type == CA_SET_LEVEL_SCORE ? game.score :
10098      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10099      action_type == CA_SET_CE_SCORE ? ei->collect_score :
10100      0);
10101
10102   int action_arg_number_new =
10103     getModifiedActionNumber(action_arg_number_old,
10104                             action_mode, action_arg_number,
10105                             action_arg_number_min, action_arg_number_max);
10106
10107   int trigger_player_bits =
10108     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10109      change->actual_trigger_player_bits : change->trigger_player);
10110
10111   int action_arg_player_bits =
10112     (action_arg >= CA_ARG_PLAYER_1 &&
10113      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10114      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10115      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10116      PLAYER_BITS_ANY);
10117
10118   // ---------- execute action  -----------------------------------------------
10119
10120   switch (action_type)
10121   {
10122     case CA_NO_ACTION:
10123     {
10124       return;
10125     }
10126
10127     // ---------- level actions  ----------------------------------------------
10128
10129     case CA_RESTART_LEVEL:
10130     {
10131       game.restart_level = TRUE;
10132
10133       break;
10134     }
10135
10136     case CA_SHOW_ENVELOPE:
10137     {
10138       int element = getSpecialActionElement(action_arg_element,
10139                                             action_arg_number, EL_ENVELOPE_1);
10140
10141       if (IS_ENVELOPE(element))
10142         local_player->show_envelope = element;
10143
10144       break;
10145     }
10146
10147     case CA_SET_LEVEL_TIME:
10148     {
10149       if (level.time > 0)       // only modify limited time value
10150       {
10151         TimeLeft = action_arg_number_new;
10152
10153         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10154
10155         DisplayGameControlValues();
10156
10157         if (!TimeLeft && game.time_limit)
10158           for (i = 0; i < MAX_PLAYERS; i++)
10159             KillPlayer(&stored_player[i]);
10160       }
10161
10162       break;
10163     }
10164
10165     case CA_SET_LEVEL_SCORE:
10166     {
10167       game.score = action_arg_number_new;
10168
10169       game_panel_controls[GAME_PANEL_SCORE].value = game.score;
10170
10171       DisplayGameControlValues();
10172
10173       break;
10174     }
10175
10176     case CA_SET_LEVEL_GEMS:
10177     {
10178       game.gems_still_needed = action_arg_number_new;
10179
10180       game.snapshot.collected_item = TRUE;
10181
10182       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
10183
10184       DisplayGameControlValues();
10185
10186       break;
10187     }
10188
10189     case CA_SET_LEVEL_WIND:
10190     {
10191       game.wind_direction = action_arg_direction;
10192
10193       break;
10194     }
10195
10196     case CA_SET_LEVEL_RANDOM_SEED:
10197     {
10198       // ensure that setting a new random seed while playing is predictable
10199       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10200
10201       break;
10202     }
10203
10204     // ---------- player actions  ---------------------------------------------
10205
10206     case CA_MOVE_PLAYER:
10207     case CA_MOVE_PLAYER_NEW:
10208     {
10209       // automatically move to the next field in specified direction
10210       for (i = 0; i < MAX_PLAYERS; i++)
10211         if (trigger_player_bits & (1 << i))
10212           if (action_type == CA_MOVE_PLAYER ||
10213               stored_player[i].MovPos == 0)
10214             stored_player[i].programmed_action = action_arg_direction;
10215
10216       break;
10217     }
10218
10219     case CA_EXIT_PLAYER:
10220     {
10221       for (i = 0; i < MAX_PLAYERS; i++)
10222         if (action_arg_player_bits & (1 << i))
10223           ExitPlayer(&stored_player[i]);
10224
10225       if (game.players_still_needed == 0)
10226         LevelSolved();
10227
10228       break;
10229     }
10230
10231     case CA_KILL_PLAYER:
10232     {
10233       for (i = 0; i < MAX_PLAYERS; i++)
10234         if (action_arg_player_bits & (1 << i))
10235           KillPlayer(&stored_player[i]);
10236
10237       break;
10238     }
10239
10240     case CA_SET_PLAYER_KEYS:
10241     {
10242       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10243       int element = getSpecialActionElement(action_arg_element,
10244                                             action_arg_number, EL_KEY_1);
10245
10246       if (IS_KEY(element))
10247       {
10248         for (i = 0; i < MAX_PLAYERS; i++)
10249         {
10250           if (trigger_player_bits & (1 << i))
10251           {
10252             stored_player[i].key[KEY_NR(element)] = key_state;
10253
10254             DrawGameDoorValues();
10255           }
10256         }
10257       }
10258
10259       break;
10260     }
10261
10262     case CA_SET_PLAYER_SPEED:
10263     {
10264       for (i = 0; i < MAX_PLAYERS; i++)
10265       {
10266         if (trigger_player_bits & (1 << i))
10267         {
10268           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10269
10270           if (action_arg == CA_ARG_SPEED_FASTER &&
10271               stored_player[i].cannot_move)
10272           {
10273             action_arg_number = STEPSIZE_VERY_SLOW;
10274           }
10275           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10276                    action_arg == CA_ARG_SPEED_FASTER)
10277           {
10278             action_arg_number = 2;
10279             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10280                            CA_MODE_MULTIPLY);
10281           }
10282           else if (action_arg == CA_ARG_NUMBER_RESET)
10283           {
10284             action_arg_number = level.initial_player_stepsize[i];
10285           }
10286
10287           move_stepsize =
10288             getModifiedActionNumber(move_stepsize,
10289                                     action_mode,
10290                                     action_arg_number,
10291                                     action_arg_number_min,
10292                                     action_arg_number_max);
10293
10294           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10295         }
10296       }
10297
10298       break;
10299     }
10300
10301     case CA_SET_PLAYER_SHIELD:
10302     {
10303       for (i = 0; i < MAX_PLAYERS; i++)
10304       {
10305         if (trigger_player_bits & (1 << i))
10306         {
10307           if (action_arg == CA_ARG_SHIELD_OFF)
10308           {
10309             stored_player[i].shield_normal_time_left = 0;
10310             stored_player[i].shield_deadly_time_left = 0;
10311           }
10312           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10313           {
10314             stored_player[i].shield_normal_time_left = 999999;
10315           }
10316           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10317           {
10318             stored_player[i].shield_normal_time_left = 999999;
10319             stored_player[i].shield_deadly_time_left = 999999;
10320           }
10321         }
10322       }
10323
10324       break;
10325     }
10326
10327     case CA_SET_PLAYER_GRAVITY:
10328     {
10329       for (i = 0; i < MAX_PLAYERS; i++)
10330       {
10331         if (trigger_player_bits & (1 << i))
10332         {
10333           stored_player[i].gravity =
10334             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10335              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10336              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10337              stored_player[i].gravity);
10338         }
10339       }
10340
10341       break;
10342     }
10343
10344     case CA_SET_PLAYER_ARTWORK:
10345     {
10346       for (i = 0; i < MAX_PLAYERS; i++)
10347       {
10348         if (trigger_player_bits & (1 << i))
10349         {
10350           int artwork_element = action_arg_element;
10351
10352           if (action_arg == CA_ARG_ELEMENT_RESET)
10353             artwork_element =
10354               (level.use_artwork_element[i] ? level.artwork_element[i] :
10355                stored_player[i].element_nr);
10356
10357           if (stored_player[i].artwork_element != artwork_element)
10358             stored_player[i].Frame = 0;
10359
10360           stored_player[i].artwork_element = artwork_element;
10361
10362           SetPlayerWaiting(&stored_player[i], FALSE);
10363
10364           // set number of special actions for bored and sleeping animation
10365           stored_player[i].num_special_action_bored =
10366             get_num_special_action(artwork_element,
10367                                    ACTION_BORING_1, ACTION_BORING_LAST);
10368           stored_player[i].num_special_action_sleeping =
10369             get_num_special_action(artwork_element,
10370                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10371         }
10372       }
10373
10374       break;
10375     }
10376
10377     case CA_SET_PLAYER_INVENTORY:
10378     {
10379       for (i = 0; i < MAX_PLAYERS; i++)
10380       {
10381         struct PlayerInfo *player = &stored_player[i];
10382         int j, k;
10383
10384         if (trigger_player_bits & (1 << i))
10385         {
10386           int inventory_element = action_arg_element;
10387
10388           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10389               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10390               action_arg == CA_ARG_ELEMENT_ACTION)
10391           {
10392             int element = inventory_element;
10393             int collect_count = element_info[element].collect_count_initial;
10394
10395             if (!IS_CUSTOM_ELEMENT(element))
10396               collect_count = 1;
10397
10398             if (collect_count == 0)
10399               player->inventory_infinite_element = element;
10400             else
10401               for (k = 0; k < collect_count; k++)
10402                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10403                   player->inventory_element[player->inventory_size++] =
10404                     element;
10405           }
10406           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10407                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10408                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10409           {
10410             if (player->inventory_infinite_element != EL_UNDEFINED &&
10411                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10412                                      action_arg_element_raw))
10413               player->inventory_infinite_element = EL_UNDEFINED;
10414
10415             for (k = 0, j = 0; j < player->inventory_size; j++)
10416             {
10417               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10418                                         action_arg_element_raw))
10419                 player->inventory_element[k++] = player->inventory_element[j];
10420             }
10421
10422             player->inventory_size = k;
10423           }
10424           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10425           {
10426             if (player->inventory_size > 0)
10427             {
10428               for (j = 0; j < player->inventory_size - 1; j++)
10429                 player->inventory_element[j] = player->inventory_element[j + 1];
10430
10431               player->inventory_size--;
10432             }
10433           }
10434           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10435           {
10436             if (player->inventory_size > 0)
10437               player->inventory_size--;
10438           }
10439           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10440           {
10441             player->inventory_infinite_element = EL_UNDEFINED;
10442             player->inventory_size = 0;
10443           }
10444           else if (action_arg == CA_ARG_INVENTORY_RESET)
10445           {
10446             player->inventory_infinite_element = EL_UNDEFINED;
10447             player->inventory_size = 0;
10448
10449             if (level.use_initial_inventory[i])
10450             {
10451               for (j = 0; j < level.initial_inventory_size[i]; j++)
10452               {
10453                 int element = level.initial_inventory_content[i][j];
10454                 int collect_count = element_info[element].collect_count_initial;
10455
10456                 if (!IS_CUSTOM_ELEMENT(element))
10457                   collect_count = 1;
10458
10459                 if (collect_count == 0)
10460                   player->inventory_infinite_element = element;
10461                 else
10462                   for (k = 0; k < collect_count; k++)
10463                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10464                       player->inventory_element[player->inventory_size++] =
10465                         element;
10466               }
10467             }
10468           }
10469         }
10470       }
10471
10472       break;
10473     }
10474
10475     // ---------- CE actions  -------------------------------------------------
10476
10477     case CA_SET_CE_VALUE:
10478     {
10479       int last_ce_value = CustomValue[x][y];
10480
10481       CustomValue[x][y] = action_arg_number_new;
10482
10483       if (CustomValue[x][y] != last_ce_value)
10484       {
10485         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10486         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10487
10488         if (CustomValue[x][y] == 0)
10489         {
10490           // reset change counter (else CE_VALUE_GETS_ZERO would not work)
10491           ChangeCount[x][y] = 0;        // allow at least one more change
10492
10493           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10494           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10495         }
10496       }
10497
10498       break;
10499     }
10500
10501     case CA_SET_CE_SCORE:
10502     {
10503       int last_ce_score = ei->collect_score;
10504
10505       ei->collect_score = action_arg_number_new;
10506
10507       if (ei->collect_score != last_ce_score)
10508       {
10509         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10510         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10511
10512         if (ei->collect_score == 0)
10513         {
10514           int xx, yy;
10515
10516           // reset change counter (else CE_SCORE_GETS_ZERO would not work)
10517           ChangeCount[x][y] = 0;        // allow at least one more change
10518
10519           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10520           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10521
10522           /*
10523             This is a very special case that seems to be a mixture between
10524             CheckElementChange() and CheckTriggeredElementChange(): while
10525             the first one only affects single elements that are triggered
10526             directly, the second one affects multiple elements in the playfield
10527             that are triggered indirectly by another element. This is a third
10528             case: Changing the CE score always affects multiple identical CEs,
10529             so every affected CE must be checked, not only the single CE for
10530             which the CE score was changed in the first place (as every instance
10531             of that CE shares the same CE score, and therefore also can change)!
10532           */
10533           SCAN_PLAYFIELD(xx, yy)
10534           {
10535             if (Tile[xx][yy] == element)
10536               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10537                                  CE_SCORE_GETS_ZERO);
10538           }
10539         }
10540       }
10541
10542       break;
10543     }
10544
10545     case CA_SET_CE_ARTWORK:
10546     {
10547       int artwork_element = action_arg_element;
10548       boolean reset_frame = FALSE;
10549       int xx, yy;
10550
10551       if (action_arg == CA_ARG_ELEMENT_RESET)
10552         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10553                            element);
10554
10555       if (ei->gfx_element != artwork_element)
10556         reset_frame = TRUE;
10557
10558       ei->gfx_element = artwork_element;
10559
10560       SCAN_PLAYFIELD(xx, yy)
10561       {
10562         if (Tile[xx][yy] == element)
10563         {
10564           if (reset_frame)
10565           {
10566             ResetGfxAnimation(xx, yy);
10567             ResetRandomAnimationValue(xx, yy);
10568           }
10569
10570           TEST_DrawLevelField(xx, yy);
10571         }
10572       }
10573
10574       break;
10575     }
10576
10577     // ---------- engine actions  ---------------------------------------------
10578
10579     case CA_SET_ENGINE_SCAN_MODE:
10580     {
10581       InitPlayfieldScanMode(action_arg);
10582
10583       break;
10584     }
10585
10586     default:
10587       break;
10588   }
10589 }
10590
10591 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10592 {
10593   int old_element = Tile[x][y];
10594   int new_element = GetElementFromGroupElement(element);
10595   int previous_move_direction = MovDir[x][y];
10596   int last_ce_value = CustomValue[x][y];
10597   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10598   boolean new_element_is_player = IS_PLAYER_ELEMENT(new_element);
10599   boolean add_player_onto_element = (new_element_is_player &&
10600                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10601                                      IS_WALKABLE(old_element));
10602
10603   if (!add_player_onto_element)
10604   {
10605     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10606       RemoveMovingField(x, y);
10607     else
10608       RemoveField(x, y);
10609
10610     Tile[x][y] = new_element;
10611
10612     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10613       MovDir[x][y] = previous_move_direction;
10614
10615     if (element_info[new_element].use_last_ce_value)
10616       CustomValue[x][y] = last_ce_value;
10617
10618     InitField_WithBug1(x, y, FALSE);
10619
10620     new_element = Tile[x][y];   // element may have changed
10621
10622     ResetGfxAnimation(x, y);
10623     ResetRandomAnimationValue(x, y);
10624
10625     TEST_DrawLevelField(x, y);
10626
10627     if (GFX_CRUMBLED(new_element))
10628       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10629   }
10630
10631   // check if element under the player changes from accessible to unaccessible
10632   // (needed for special case of dropping element which then changes)
10633   // (must be checked after creating new element for walkable group elements)
10634   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10635       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10636   {
10637     Bang(x, y);
10638
10639     return;
10640   }
10641
10642   // "ChangeCount" not set yet to allow "entered by player" change one time
10643   if (new_element_is_player)
10644     RelocatePlayer(x, y, new_element);
10645
10646   if (is_change)
10647     ChangeCount[x][y]++;        // count number of changes in the same frame
10648
10649   TestIfBadThingTouchesPlayer(x, y);
10650   TestIfPlayerTouchesCustomElement(x, y);
10651   TestIfElementTouchesCustomElement(x, y);
10652 }
10653
10654 static void CreateField(int x, int y, int element)
10655 {
10656   CreateFieldExt(x, y, element, FALSE);
10657 }
10658
10659 static void CreateElementFromChange(int x, int y, int element)
10660 {
10661   element = GET_VALID_RUNTIME_ELEMENT(element);
10662
10663   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10664   {
10665     int old_element = Tile[x][y];
10666
10667     // prevent changed element from moving in same engine frame
10668     // unless both old and new element can either fall or move
10669     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10670         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10671       Stop[x][y] = TRUE;
10672   }
10673
10674   CreateFieldExt(x, y, element, TRUE);
10675 }
10676
10677 static boolean ChangeElement(int x, int y, int element, int page)
10678 {
10679   struct ElementInfo *ei = &element_info[element];
10680   struct ElementChangeInfo *change = &ei->change_page[page];
10681   int ce_value = CustomValue[x][y];
10682   int ce_score = ei->collect_score;
10683   int target_element;
10684   int old_element = Tile[x][y];
10685
10686   // always use default change event to prevent running into a loop
10687   if (ChangeEvent[x][y] == -1)
10688     ChangeEvent[x][y] = CE_DELAY;
10689
10690   if (ChangeEvent[x][y] == CE_DELAY)
10691   {
10692     // reset actual trigger element, trigger player and action element
10693     change->actual_trigger_element = EL_EMPTY;
10694     change->actual_trigger_player = EL_EMPTY;
10695     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10696     change->actual_trigger_side = CH_SIDE_NONE;
10697     change->actual_trigger_ce_value = 0;
10698     change->actual_trigger_ce_score = 0;
10699   }
10700
10701   // do not change elements more than a specified maximum number of changes
10702   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10703     return FALSE;
10704
10705   ChangeCount[x][y]++;          // count number of changes in the same frame
10706
10707   if (change->explode)
10708   {
10709     Bang(x, y);
10710
10711     return TRUE;
10712   }
10713
10714   if (change->use_target_content)
10715   {
10716     boolean complete_replace = TRUE;
10717     boolean can_replace[3][3];
10718     int xx, yy;
10719
10720     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10721     {
10722       boolean is_empty;
10723       boolean is_walkable;
10724       boolean is_diggable;
10725       boolean is_collectible;
10726       boolean is_removable;
10727       boolean is_destructible;
10728       int ex = x + xx - 1;
10729       int ey = y + yy - 1;
10730       int content_element = change->target_content.e[xx][yy];
10731       int e;
10732
10733       can_replace[xx][yy] = TRUE;
10734
10735       if (ex == x && ey == y)   // do not check changing element itself
10736         continue;
10737
10738       if (content_element == EL_EMPTY_SPACE)
10739       {
10740         can_replace[xx][yy] = FALSE;    // do not replace border with space
10741
10742         continue;
10743       }
10744
10745       if (!IN_LEV_FIELD(ex, ey))
10746       {
10747         can_replace[xx][yy] = FALSE;
10748         complete_replace = FALSE;
10749
10750         continue;
10751       }
10752
10753       e = Tile[ex][ey];
10754
10755       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10756         e = MovingOrBlocked2Element(ex, ey);
10757
10758       is_empty = (IS_FREE(ex, ey) ||
10759                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10760
10761       is_walkable     = (is_empty || IS_WALKABLE(e));
10762       is_diggable     = (is_empty || IS_DIGGABLE(e));
10763       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10764       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10765       is_removable    = (is_diggable || is_collectible);
10766
10767       can_replace[xx][yy] =
10768         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10769           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10770           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10771           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10772           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10773           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10774          !(IS_PLAYER(ex, ey) && IS_PLAYER_ELEMENT(content_element)));
10775
10776       if (!can_replace[xx][yy])
10777         complete_replace = FALSE;
10778     }
10779
10780     if (!change->only_if_complete || complete_replace)
10781     {
10782       boolean something_has_changed = FALSE;
10783
10784       if (change->only_if_complete && change->use_random_replace &&
10785           RND(100) < change->random_percentage)
10786         return FALSE;
10787
10788       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10789       {
10790         int ex = x + xx - 1;
10791         int ey = y + yy - 1;
10792         int content_element;
10793
10794         if (can_replace[xx][yy] && (!change->use_random_replace ||
10795                                     RND(100) < change->random_percentage))
10796         {
10797           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10798             RemoveMovingField(ex, ey);
10799
10800           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10801
10802           content_element = change->target_content.e[xx][yy];
10803           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10804                                               ce_value, ce_score);
10805
10806           CreateElementFromChange(ex, ey, target_element);
10807
10808           something_has_changed = TRUE;
10809
10810           // for symmetry reasons, freeze newly created border elements
10811           if (ex != x || ey != y)
10812             Stop[ex][ey] = TRUE;        // no more moving in this frame
10813         }
10814       }
10815
10816       if (something_has_changed)
10817       {
10818         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10819         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10820       }
10821     }
10822   }
10823   else
10824   {
10825     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10826                                         ce_value, ce_score);
10827
10828     if (element == EL_DIAGONAL_GROWING ||
10829         element == EL_DIAGONAL_SHRINKING)
10830     {
10831       target_element = Store[x][y];
10832
10833       Store[x][y] = EL_EMPTY;
10834     }
10835
10836     // special case: element changes to player (and may be kept if walkable)
10837     if (IS_PLAYER_ELEMENT(target_element) && !level.keep_walkable_ce)
10838       CreateElementFromChange(x, y, EL_EMPTY);
10839
10840     CreateElementFromChange(x, y, target_element);
10841
10842     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10843     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10844   }
10845
10846   // this uses direct change before indirect change
10847   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10848
10849   return TRUE;
10850 }
10851
10852 static void HandleElementChange(int x, int y, int page)
10853 {
10854   int element = MovingOrBlocked2Element(x, y);
10855   struct ElementInfo *ei = &element_info[element];
10856   struct ElementChangeInfo *change = &ei->change_page[page];
10857   boolean handle_action_before_change = FALSE;
10858
10859 #ifdef DEBUG
10860   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10861       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10862   {
10863     Debug("game:playing:HandleElementChange", "%d,%d: element = %d ('%s')",
10864           x, y, element, element_info[element].token_name);
10865     Debug("game:playing:HandleElementChange", "This should never happen!");
10866   }
10867 #endif
10868
10869   // this can happen with classic bombs on walkable, changing elements
10870   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10871   {
10872     return;
10873   }
10874
10875   if (ChangeDelay[x][y] == 0)           // initialize element change
10876   {
10877     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10878
10879     if (change->can_change)
10880     {
10881       // !!! not clear why graphic animation should be reset at all here !!!
10882       // !!! UPDATE: but is needed for correct Snake Bite tail animation !!!
10883       // !!! SOLUTION: do not reset if graphics engine set to 4 or above !!!
10884
10885       /*
10886         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10887
10888         When using an animation frame delay of 1 (this only happens with
10889         "sp_zonk.moving.left/right" in the classic graphics), the default
10890         (non-moving) animation shows wrong animation frames (while the
10891         moving animation, like "sp_zonk.moving.left/right", is correct,
10892         so this graphical bug never shows up with the classic graphics).
10893         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10894         be drawn instead of the correct frames 0,1,2,3. This is caused by
10895         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10896         an element change: First when the change delay ("ChangeDelay[][]")
10897         counter has reached zero after decrementing, then a second time in
10898         the next frame (after "GfxFrame[][]" was already incremented) when
10899         "ChangeDelay[][]" is reset to the initial delay value again.
10900
10901         This causes frame 0 to be drawn twice, while the last frame won't
10902         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10903
10904         As some animations may already be cleverly designed around this bug
10905         (at least the "Snake Bite" snake tail animation does this), it cannot
10906         simply be fixed here without breaking such existing animations.
10907         Unfortunately, it cannot easily be detected if a graphics set was
10908         designed "before" or "after" the bug was fixed. As a workaround,
10909         a new graphics set option "game.graphics_engine_version" was added
10910         to be able to specify the game's major release version for which the
10911         graphics set was designed, which can then be used to decide if the
10912         bugfix should be used (version 4 and above) or not (version 3 or
10913         below, or if no version was specified at all, as with old sets).
10914
10915         (The wrong/fixed animation frames can be tested with the test level set
10916         "test_gfxframe" and level "000", which contains a specially prepared
10917         custom element at level position (x/y) == (11/9) which uses the zonk
10918         animation mentioned above. Using "game.graphics_engine_version: 4"
10919         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10920         This can also be seen from the debug output for this test element.)
10921       */
10922
10923       // when a custom element is about to change (for example by change delay),
10924       // do not reset graphic animation when the custom element is moving
10925       if (game.graphics_engine_version < 4 &&
10926           !IS_MOVING(x, y))
10927       {
10928         ResetGfxAnimation(x, y);
10929         ResetRandomAnimationValue(x, y);
10930       }
10931
10932       if (change->pre_change_function)
10933         change->pre_change_function(x, y);
10934     }
10935   }
10936
10937   ChangeDelay[x][y]--;
10938
10939   if (ChangeDelay[x][y] != 0)           // continue element change
10940   {
10941     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10942
10943     // also needed if CE can not change, but has CE delay with CE action
10944     if (IS_ANIMATED(graphic))
10945       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10946
10947     if (change->can_change)
10948     {
10949       if (change->change_function)
10950         change->change_function(x, y);
10951     }
10952   }
10953   else                                  // finish element change
10954   {
10955     if (ChangePage[x][y] != -1)         // remember page from delayed change
10956     {
10957       page = ChangePage[x][y];
10958       ChangePage[x][y] = -1;
10959
10960       change = &ei->change_page[page];
10961     }
10962
10963     if (IS_MOVING(x, y))                // never change a running system ;-)
10964     {
10965       ChangeDelay[x][y] = 1;            // try change after next move step
10966       ChangePage[x][y] = page;          // remember page to use for change
10967
10968       return;
10969     }
10970
10971     // special case: set new level random seed before changing element
10972     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10973       handle_action_before_change = TRUE;
10974
10975     if (change->has_action && handle_action_before_change)
10976       ExecuteCustomElementAction(x, y, element, page);
10977
10978     if (change->can_change)
10979     {
10980       if (ChangeElement(x, y, element, page))
10981       {
10982         if (change->post_change_function)
10983           change->post_change_function(x, y);
10984       }
10985     }
10986
10987     if (change->has_action && !handle_action_before_change)
10988       ExecuteCustomElementAction(x, y, element, page);
10989   }
10990 }
10991
10992 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10993                                               int trigger_element,
10994                                               int trigger_event,
10995                                               int trigger_player,
10996                                               int trigger_side,
10997                                               int trigger_page)
10998 {
10999   boolean change_done_any = FALSE;
11000   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11001   int i;
11002
11003   if (!(trigger_events[trigger_element][trigger_event]))
11004     return FALSE;
11005
11006   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11007
11008   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11009   {
11010     int element = EL_CUSTOM_START + i;
11011     boolean change_done = FALSE;
11012     int p;
11013
11014     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11015         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11016       continue;
11017
11018     for (p = 0; p < element_info[element].num_change_pages; p++)
11019     {
11020       struct ElementChangeInfo *change = &element_info[element].change_page[p];
11021
11022       if (change->can_change_or_has_action &&
11023           change->has_event[trigger_event] &&
11024           change->trigger_side & trigger_side &&
11025           change->trigger_player & trigger_player &&
11026           change->trigger_page & trigger_page_bits &&
11027           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11028       {
11029         change->actual_trigger_element = trigger_element;
11030         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11031         change->actual_trigger_player_bits = trigger_player;
11032         change->actual_trigger_side = trigger_side;
11033         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11034         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11035
11036         if ((change->can_change && !change_done) || change->has_action)
11037         {
11038           int x, y;
11039
11040           SCAN_PLAYFIELD(x, y)
11041           {
11042             if (Tile[x][y] == element)
11043             {
11044               if (change->can_change && !change_done)
11045               {
11046                 // if element already changed in this frame, not only prevent
11047                 // another element change (checked in ChangeElement()), but
11048                 // also prevent additional element actions for this element
11049
11050                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11051                     !level.use_action_after_change_bug)
11052                   continue;
11053
11054                 ChangeDelay[x][y] = 1;
11055                 ChangeEvent[x][y] = trigger_event;
11056
11057                 HandleElementChange(x, y, p);
11058               }
11059               else if (change->has_action)
11060               {
11061                 // if element already changed in this frame, not only prevent
11062                 // another element change (checked in ChangeElement()), but
11063                 // also prevent additional element actions for this element
11064
11065                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11066                     !level.use_action_after_change_bug)
11067                   continue;
11068
11069                 ExecuteCustomElementAction(x, y, element, p);
11070                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11071               }
11072             }
11073           }
11074
11075           if (change->can_change)
11076           {
11077             change_done = TRUE;
11078             change_done_any = TRUE;
11079           }
11080         }
11081       }
11082     }
11083   }
11084
11085   RECURSION_LOOP_DETECTION_END();
11086
11087   return change_done_any;
11088 }
11089
11090 static boolean CheckElementChangeExt(int x, int y,
11091                                      int element,
11092                                      int trigger_element,
11093                                      int trigger_event,
11094                                      int trigger_player,
11095                                      int trigger_side)
11096 {
11097   boolean change_done = FALSE;
11098   int p;
11099
11100   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11101       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11102     return FALSE;
11103
11104   if (Tile[x][y] == EL_BLOCKED)
11105   {
11106     Blocked2Moving(x, y, &x, &y);
11107     element = Tile[x][y];
11108   }
11109
11110   // check if element has already changed or is about to change after moving
11111   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11112        Tile[x][y] != element) ||
11113
11114       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11115        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11116         ChangePage[x][y] != -1)))
11117     return FALSE;
11118
11119   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11120
11121   for (p = 0; p < element_info[element].num_change_pages; p++)
11122   {
11123     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11124
11125     /* check trigger element for all events where the element that is checked
11126        for changing interacts with a directly adjacent element -- this is
11127        different to element changes that affect other elements to change on the
11128        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11129     boolean check_trigger_element =
11130       (trigger_event == CE_NEXT_TO_X ||
11131        trigger_event == CE_TOUCHING_X ||
11132        trigger_event == CE_HITTING_X ||
11133        trigger_event == CE_HIT_BY_X ||
11134        trigger_event == CE_DIGGING_X); // this one was forgotten until 3.2.3
11135
11136     if (change->can_change_or_has_action &&
11137         change->has_event[trigger_event] &&
11138         change->trigger_side & trigger_side &&
11139         change->trigger_player & trigger_player &&
11140         (!check_trigger_element ||
11141          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11142     {
11143       change->actual_trigger_element = trigger_element;
11144       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11145       change->actual_trigger_player_bits = trigger_player;
11146       change->actual_trigger_side = trigger_side;
11147       change->actual_trigger_ce_value = CustomValue[x][y];
11148       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11149
11150       // special case: trigger element not at (x,y) position for some events
11151       if (check_trigger_element)
11152       {
11153         static struct
11154         {
11155           int dx, dy;
11156         } move_xy[] =
11157           {
11158             {  0,  0 },
11159             { -1,  0 },
11160             { +1,  0 },
11161             {  0,  0 },
11162             {  0, -1 },
11163             {  0,  0 }, { 0, 0 }, { 0, 0 },
11164             {  0, +1 }
11165           };
11166
11167         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11168         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11169
11170         change->actual_trigger_ce_value = CustomValue[xx][yy];
11171         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11172       }
11173
11174       if (change->can_change && !change_done)
11175       {
11176         ChangeDelay[x][y] = 1;
11177         ChangeEvent[x][y] = trigger_event;
11178
11179         HandleElementChange(x, y, p);
11180
11181         change_done = TRUE;
11182       }
11183       else if (change->has_action)
11184       {
11185         ExecuteCustomElementAction(x, y, element, p);
11186         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11187       }
11188     }
11189   }
11190
11191   RECURSION_LOOP_DETECTION_END();
11192
11193   return change_done;
11194 }
11195
11196 static void PlayPlayerSound(struct PlayerInfo *player)
11197 {
11198   int jx = player->jx, jy = player->jy;
11199   int sound_element = player->artwork_element;
11200   int last_action = player->last_action_waiting;
11201   int action = player->action_waiting;
11202
11203   if (player->is_waiting)
11204   {
11205     if (action != last_action)
11206       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11207     else
11208       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11209   }
11210   else
11211   {
11212     if (action != last_action)
11213       StopSound(element_info[sound_element].sound[last_action]);
11214
11215     if (last_action == ACTION_SLEEPING)
11216       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11217   }
11218 }
11219
11220 static void PlayAllPlayersSound(void)
11221 {
11222   int i;
11223
11224   for (i = 0; i < MAX_PLAYERS; i++)
11225     if (stored_player[i].active)
11226       PlayPlayerSound(&stored_player[i]);
11227 }
11228
11229 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11230 {
11231   boolean last_waiting = player->is_waiting;
11232   int move_dir = player->MovDir;
11233
11234   player->dir_waiting = move_dir;
11235   player->last_action_waiting = player->action_waiting;
11236
11237   if (is_waiting)
11238   {
11239     if (!last_waiting)          // not waiting -> waiting
11240     {
11241       player->is_waiting = TRUE;
11242
11243       player->frame_counter_bored =
11244         FrameCounter +
11245         game.player_boring_delay_fixed +
11246         GetSimpleRandom(game.player_boring_delay_random);
11247       player->frame_counter_sleeping =
11248         FrameCounter +
11249         game.player_sleeping_delay_fixed +
11250         GetSimpleRandom(game.player_sleeping_delay_random);
11251
11252       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11253     }
11254
11255     if (game.player_sleeping_delay_fixed +
11256         game.player_sleeping_delay_random > 0 &&
11257         player->anim_delay_counter == 0 &&
11258         player->post_delay_counter == 0 &&
11259         FrameCounter >= player->frame_counter_sleeping)
11260       player->is_sleeping = TRUE;
11261     else if (game.player_boring_delay_fixed +
11262              game.player_boring_delay_random > 0 &&
11263              FrameCounter >= player->frame_counter_bored)
11264       player->is_bored = TRUE;
11265
11266     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11267                               player->is_bored ? ACTION_BORING :
11268                               ACTION_WAITING);
11269
11270     if (player->is_sleeping && player->use_murphy)
11271     {
11272       // special case for sleeping Murphy when leaning against non-free tile
11273
11274       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11275           (Tile[player->jx - 1][player->jy] != EL_EMPTY &&
11276            !IS_MOVING(player->jx - 1, player->jy)))
11277         move_dir = MV_LEFT;
11278       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11279                (Tile[player->jx + 1][player->jy] != EL_EMPTY &&
11280                 !IS_MOVING(player->jx + 1, player->jy)))
11281         move_dir = MV_RIGHT;
11282       else
11283         player->is_sleeping = FALSE;
11284
11285       player->dir_waiting = move_dir;
11286     }
11287
11288     if (player->is_sleeping)
11289     {
11290       if (player->num_special_action_sleeping > 0)
11291       {
11292         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11293         {
11294           int last_special_action = player->special_action_sleeping;
11295           int num_special_action = player->num_special_action_sleeping;
11296           int special_action =
11297             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11298              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11299              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11300              last_special_action + 1 : ACTION_SLEEPING);
11301           int special_graphic =
11302             el_act_dir2img(player->artwork_element, special_action, move_dir);
11303
11304           player->anim_delay_counter =
11305             graphic_info[special_graphic].anim_delay_fixed +
11306             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11307           player->post_delay_counter =
11308             graphic_info[special_graphic].post_delay_fixed +
11309             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11310
11311           player->special_action_sleeping = special_action;
11312         }
11313
11314         if (player->anim_delay_counter > 0)
11315         {
11316           player->action_waiting = player->special_action_sleeping;
11317           player->anim_delay_counter--;
11318         }
11319         else if (player->post_delay_counter > 0)
11320         {
11321           player->post_delay_counter--;
11322         }
11323       }
11324     }
11325     else if (player->is_bored)
11326     {
11327       if (player->num_special_action_bored > 0)
11328       {
11329         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11330         {
11331           int special_action =
11332             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11333           int special_graphic =
11334             el_act_dir2img(player->artwork_element, special_action, move_dir);
11335
11336           player->anim_delay_counter =
11337             graphic_info[special_graphic].anim_delay_fixed +
11338             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11339           player->post_delay_counter =
11340             graphic_info[special_graphic].post_delay_fixed +
11341             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11342
11343           player->special_action_bored = special_action;
11344         }
11345
11346         if (player->anim_delay_counter > 0)
11347         {
11348           player->action_waiting = player->special_action_bored;
11349           player->anim_delay_counter--;
11350         }
11351         else if (player->post_delay_counter > 0)
11352         {
11353           player->post_delay_counter--;
11354         }
11355       }
11356     }
11357   }
11358   else if (last_waiting)        // waiting -> not waiting
11359   {
11360     player->is_waiting = FALSE;
11361     player->is_bored = FALSE;
11362     player->is_sleeping = FALSE;
11363
11364     player->frame_counter_bored = -1;
11365     player->frame_counter_sleeping = -1;
11366
11367     player->anim_delay_counter = 0;
11368     player->post_delay_counter = 0;
11369
11370     player->dir_waiting = player->MovDir;
11371     player->action_waiting = ACTION_DEFAULT;
11372
11373     player->special_action_bored = ACTION_DEFAULT;
11374     player->special_action_sleeping = ACTION_DEFAULT;
11375   }
11376 }
11377
11378 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
11379 {
11380   if ((!player->is_moving  && player->was_moving) ||
11381       (player->MovPos == 0 && player->was_moving) ||
11382       (player->is_snapping && !player->was_snapping) ||
11383       (player->is_dropping && !player->was_dropping))
11384   {
11385     if (!CheckSaveEngineSnapshotToList())
11386       return;
11387
11388     player->was_moving = FALSE;
11389     player->was_snapping = TRUE;
11390     player->was_dropping = TRUE;
11391   }
11392   else
11393   {
11394     if (player->is_moving)
11395       player->was_moving = TRUE;
11396
11397     if (!player->is_snapping)
11398       player->was_snapping = FALSE;
11399
11400     if (!player->is_dropping)
11401       player->was_dropping = FALSE;
11402   }
11403
11404   static struct MouseActionInfo mouse_action_last = { 0 };
11405   struct MouseActionInfo mouse_action = player->effective_mouse_action;
11406   boolean new_released = (!mouse_action.button && mouse_action_last.button);
11407
11408   if (new_released)
11409     CheckSaveEngineSnapshotToList();
11410
11411   mouse_action_last = mouse_action;
11412 }
11413
11414 static void CheckSingleStepMode(struct PlayerInfo *player)
11415 {
11416   if (tape.single_step && tape.recording && !tape.pausing)
11417   {
11418     // as it is called "single step mode", just return to pause mode when the
11419     // player stopped moving after one tile (or never starts moving at all)
11420     // (reverse logic needed here in case single step mode used in team mode)
11421     if (player->is_moving ||
11422         player->is_pushing ||
11423         player->is_dropping_pressed ||
11424         player->effective_mouse_action.button)
11425       game.enter_single_step_mode = FALSE;
11426   }
11427
11428   CheckSaveEngineSnapshot(player);
11429 }
11430
11431 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11432 {
11433   int left      = player_action & JOY_LEFT;
11434   int right     = player_action & JOY_RIGHT;
11435   int up        = player_action & JOY_UP;
11436   int down      = player_action & JOY_DOWN;
11437   int button1   = player_action & JOY_BUTTON_1;
11438   int button2   = player_action & JOY_BUTTON_2;
11439   int dx        = (left ? -1 : right ? 1 : 0);
11440   int dy        = (up   ? -1 : down  ? 1 : 0);
11441
11442   if (!player->active || tape.pausing)
11443     return 0;
11444
11445   if (player_action)
11446   {
11447     if (button1)
11448       SnapField(player, dx, dy);
11449     else
11450     {
11451       if (button2)
11452         DropElement(player);
11453
11454       MovePlayer(player, dx, dy);
11455     }
11456
11457     CheckSingleStepMode(player);
11458
11459     SetPlayerWaiting(player, FALSE);
11460
11461     return player_action;
11462   }
11463   else
11464   {
11465     // no actions for this player (no input at player's configured device)
11466
11467     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11468     SnapField(player, 0, 0);
11469     CheckGravityMovementWhenNotMoving(player);
11470
11471     if (player->MovPos == 0)
11472       SetPlayerWaiting(player, TRUE);
11473
11474     if (player->MovPos == 0)    // needed for tape.playing
11475       player->is_moving = FALSE;
11476
11477     player->is_dropping = FALSE;
11478     player->is_dropping_pressed = FALSE;
11479     player->drop_pressed_delay = 0;
11480
11481     CheckSingleStepMode(player);
11482
11483     return 0;
11484   }
11485 }
11486
11487 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11488                                          byte *tape_action)
11489 {
11490   if (!tape.use_mouse_actions)
11491     return;
11492
11493   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11494   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11495   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11496 }
11497
11498 static void SetTapeActionFromMouseAction(byte *tape_action,
11499                                          struct MouseActionInfo *mouse_action)
11500 {
11501   if (!tape.use_mouse_actions)
11502     return;
11503
11504   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11505   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11506   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11507 }
11508
11509 static void CheckLevelSolved(void)
11510 {
11511   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11512   {
11513     if (game_em.level_solved &&
11514         !game_em.game_over)                             // game won
11515     {
11516       LevelSolved();
11517
11518       game_em.game_over = TRUE;
11519
11520       game.all_players_gone = TRUE;
11521     }
11522
11523     if (game_em.game_over)                              // game lost
11524       game.all_players_gone = TRUE;
11525   }
11526   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11527   {
11528     if (game_sp.level_solved &&
11529         !game_sp.game_over)                             // game won
11530     {
11531       LevelSolved();
11532
11533       game_sp.game_over = TRUE;
11534
11535       game.all_players_gone = TRUE;
11536     }
11537
11538     if (game_sp.game_over)                              // game lost
11539       game.all_players_gone = TRUE;
11540   }
11541   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11542   {
11543     if (game_mm.level_solved &&
11544         !game_mm.game_over)                             // game won
11545     {
11546       LevelSolved();
11547
11548       game_mm.game_over = TRUE;
11549
11550       game.all_players_gone = TRUE;
11551     }
11552
11553     if (game_mm.game_over)                              // game lost
11554       game.all_players_gone = TRUE;
11555   }
11556 }
11557
11558 static void CheckLevelTime_StepCounter(void)
11559 {
11560   int i;
11561
11562   TimePlayed++;
11563
11564   if (TimeLeft > 0)
11565   {
11566     TimeLeft--;
11567
11568     if (TimeLeft <= 10 && game.time_limit && !game.LevelSolved)
11569       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11570
11571     game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11572
11573     DisplayGameControlValues();
11574
11575     if (!TimeLeft && game.time_limit && !game.LevelSolved)
11576       for (i = 0; i < MAX_PLAYERS; i++)
11577         KillPlayer(&stored_player[i]);
11578   }
11579   else if (game.no_level_time_limit && !game.all_players_gone)
11580   {
11581     game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11582
11583     DisplayGameControlValues();
11584   }
11585 }
11586
11587 static void CheckLevelTime(void)
11588 {
11589   int i;
11590
11591   if (TimeFrames >= FRAMES_PER_SECOND)
11592   {
11593     TimeFrames = 0;
11594     TapeTime++;
11595
11596     for (i = 0; i < MAX_PLAYERS; i++)
11597     {
11598       struct PlayerInfo *player = &stored_player[i];
11599
11600       if (SHIELD_ON(player))
11601       {
11602         player->shield_normal_time_left--;
11603
11604         if (player->shield_deadly_time_left > 0)
11605           player->shield_deadly_time_left--;
11606       }
11607     }
11608
11609     if (!game.LevelSolved && !level.use_step_counter)
11610     {
11611       TimePlayed++;
11612
11613       if (TimeLeft > 0)
11614       {
11615         TimeLeft--;
11616
11617         if (TimeLeft <= 10 && game.time_limit)
11618           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11619
11620         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11621            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11622
11623         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11624
11625         if (!TimeLeft && game.time_limit)
11626         {
11627           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11628             game_em.lev->killed_out_of_time = TRUE;
11629           else
11630             for (i = 0; i < MAX_PLAYERS; i++)
11631               KillPlayer(&stored_player[i]);
11632         }
11633       }
11634       else if (game.no_level_time_limit && !game.all_players_gone)
11635       {
11636         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11637       }
11638
11639       game_em.lev->time = (game.no_level_time_limit ? TimePlayed : TimeLeft);
11640     }
11641
11642     if (tape.recording || tape.playing)
11643       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11644   }
11645
11646   if (tape.recording || tape.playing)
11647     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11648
11649   UpdateAndDisplayGameControlValues();
11650 }
11651
11652 void AdvanceFrameAndPlayerCounters(int player_nr)
11653 {
11654   int i;
11655
11656   // advance frame counters (global frame counter and time frame counter)
11657   FrameCounter++;
11658   TimeFrames++;
11659
11660   // advance player counters (counters for move delay, move animation etc.)
11661   for (i = 0; i < MAX_PLAYERS; i++)
11662   {
11663     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11664     int move_delay_value = stored_player[i].move_delay_value;
11665     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11666
11667     if (!advance_player_counters)       // not all players may be affected
11668       continue;
11669
11670     if (move_frames == 0)       // less than one move per game frame
11671     {
11672       int stepsize = TILEX / move_delay_value;
11673       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11674       int count = (stored_player[i].is_moving ?
11675                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11676
11677       if (count % delay == 0)
11678         move_frames = 1;
11679     }
11680
11681     stored_player[i].Frame += move_frames;
11682
11683     if (stored_player[i].MovPos != 0)
11684       stored_player[i].StepFrame += move_frames;
11685
11686     if (stored_player[i].move_delay > 0)
11687       stored_player[i].move_delay--;
11688
11689     // due to bugs in previous versions, counter must count up, not down
11690     if (stored_player[i].push_delay != -1)
11691       stored_player[i].push_delay++;
11692
11693     if (stored_player[i].drop_delay > 0)
11694       stored_player[i].drop_delay--;
11695
11696     if (stored_player[i].is_dropping_pressed)
11697       stored_player[i].drop_pressed_delay++;
11698   }
11699 }
11700
11701 void AdvanceFrameCounter(void)
11702 {
11703   FrameCounter++;
11704 }
11705
11706 void AdvanceGfxFrame(void)
11707 {
11708   int x, y;
11709
11710   SCAN_PLAYFIELD(x, y)
11711   {
11712     GfxFrame[x][y]++;
11713   }
11714 }
11715
11716 void StartGameActions(boolean init_network_game, boolean record_tape,
11717                       int random_seed)
11718 {
11719   unsigned int new_random_seed = InitRND(random_seed);
11720
11721   if (record_tape)
11722     TapeStartRecording(new_random_seed);
11723
11724   if (setup.auto_pause_on_start && !tape.pausing)
11725     TapeTogglePause(TAPE_TOGGLE_MANUAL);
11726
11727   if (init_network_game)
11728   {
11729     SendToServer_LevelFile();
11730     SendToServer_StartPlaying();
11731
11732     return;
11733   }
11734
11735   InitGame();
11736 }
11737
11738 static void GameActionsExt(void)
11739 {
11740 #if 0
11741   static unsigned int game_frame_delay = 0;
11742 #endif
11743   unsigned int game_frame_delay_value;
11744   byte *recorded_player_action;
11745   byte summarized_player_action = 0;
11746   byte tape_action[MAX_TAPE_ACTIONS] = { 0 };
11747   int i;
11748
11749   // detect endless loops, caused by custom element programming
11750   if (recursion_loop_detected && recursion_loop_depth == 0)
11751   {
11752     char *message = getStringCat3("Internal Error! Element ",
11753                                   EL_NAME(recursion_loop_element),
11754                                   " caused endless loop! Quit the game?");
11755
11756     Warn("element '%s' caused endless loop in game engine",
11757          EL_NAME(recursion_loop_element));
11758
11759     RequestQuitGameExt(program.headless, level_editor_test_game, message);
11760
11761     recursion_loop_detected = FALSE;    // if game should be continued
11762
11763     free(message);
11764
11765     return;
11766   }
11767
11768   if (game.restart_level)
11769     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11770
11771   CheckLevelSolved();
11772
11773   if (game.LevelSolved && !game.LevelSolved_GameEnd)
11774     GameWon();
11775
11776   if (game.all_players_gone && !TAPE_IS_STOPPED(tape))
11777     TapeStop();
11778
11779   if (game_status != GAME_MODE_PLAYING)         // status might have changed
11780     return;
11781
11782   game_frame_delay_value =
11783     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11784
11785   if (tape.playing && tape.warp_forward && !tape.pausing)
11786     game_frame_delay_value = 0;
11787
11788   SetVideoFrameDelay(game_frame_delay_value);
11789
11790   // (de)activate virtual buttons depending on current game status
11791   if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
11792   {
11793     if (game.all_players_gone)  // if no players there to be controlled anymore
11794       SetOverlayActive(FALSE);
11795     else if (!tape.playing)     // if game continues after tape stopped playing
11796       SetOverlayActive(TRUE);
11797   }
11798
11799 #if 0
11800 #if 0
11801   // ---------- main game synchronization point ----------
11802
11803   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11804
11805   Debug("game:playing:skip", "skip == %d", skip);
11806
11807 #else
11808   // ---------- main game synchronization point ----------
11809
11810   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11811 #endif
11812 #endif
11813
11814   if (network_playing && !network_player_action_received)
11815   {
11816     // try to get network player actions in time
11817
11818     // last chance to get network player actions without main loop delay
11819     HandleNetworking();
11820
11821     // game was quit by network peer
11822     if (game_status != GAME_MODE_PLAYING)
11823       return;
11824
11825     // check if network player actions still missing and game still running
11826     if (!network_player_action_received && !checkGameEnded())
11827       return;           // failed to get network player actions in time
11828
11829     // do not yet reset "network_player_action_received" (for tape.pausing)
11830   }
11831
11832   if (tape.pausing)
11833     return;
11834
11835   // at this point we know that we really continue executing the game
11836
11837   network_player_action_received = FALSE;
11838
11839   // when playing tape, read previously recorded player input from tape data
11840   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11841
11842   local_player->effective_mouse_action = local_player->mouse_action;
11843
11844   if (recorded_player_action != NULL)
11845     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11846                                  recorded_player_action);
11847
11848   // TapePlayAction() may return NULL when toggling to "pause before death"
11849   if (tape.pausing)
11850     return;
11851
11852   if (tape.set_centered_player)
11853   {
11854     game.centered_player_nr_next = tape.centered_player_nr_next;
11855     game.set_centered_player = TRUE;
11856   }
11857
11858   for (i = 0; i < MAX_PLAYERS; i++)
11859   {
11860     summarized_player_action |= stored_player[i].action;
11861
11862     if (!network_playing && (game.team_mode || tape.playing))
11863       stored_player[i].effective_action = stored_player[i].action;
11864   }
11865
11866   if (network_playing && !checkGameEnded())
11867     SendToServer_MovePlayer(summarized_player_action);
11868
11869   // summarize all actions at local players mapped input device position
11870   // (this allows using different input devices in single player mode)
11871   if (!network.enabled && !game.team_mode)
11872     stored_player[map_player_action[local_player->index_nr]].effective_action =
11873       summarized_player_action;
11874
11875   // summarize all actions at centered player in local team mode
11876   if (tape.recording &&
11877       setup.team_mode && !network.enabled &&
11878       setup.input_on_focus &&
11879       game.centered_player_nr != -1)
11880   {
11881     for (i = 0; i < MAX_PLAYERS; i++)
11882       stored_player[map_player_action[i]].effective_action =
11883         (i == game.centered_player_nr ? summarized_player_action : 0);
11884   }
11885
11886   if (recorded_player_action != NULL)
11887     for (i = 0; i < MAX_PLAYERS; i++)
11888       stored_player[i].effective_action = recorded_player_action[i];
11889
11890   for (i = 0; i < MAX_PLAYERS; i++)
11891   {
11892     tape_action[i] = stored_player[i].effective_action;
11893
11894     /* (this may happen in the RND game engine if a player was not present on
11895        the playfield on level start, but appeared later from a custom element */
11896     if (setup.team_mode &&
11897         tape.recording &&
11898         tape_action[i] &&
11899         !tape.player_participates[i])
11900       tape.player_participates[i] = TRUE;
11901   }
11902
11903   SetTapeActionFromMouseAction(tape_action,
11904                                &local_player->effective_mouse_action);
11905
11906   // only record actions from input devices, but not programmed actions
11907   if (tape.recording)
11908     TapeRecordAction(tape_action);
11909
11910   // remember if game was played (especially after tape stopped playing)
11911   if (!tape.playing && summarized_player_action && !checkGameFailed())
11912     game.GamePlayed = TRUE;
11913
11914 #if USE_NEW_PLAYER_ASSIGNMENTS
11915   // !!! also map player actions in single player mode !!!
11916   // if (game.team_mode)
11917   if (1)
11918   {
11919     byte mapped_action[MAX_PLAYERS];
11920
11921 #if DEBUG_PLAYER_ACTIONS
11922     for (i = 0; i < MAX_PLAYERS; i++)
11923       DebugContinued("", "%d, ", stored_player[i].effective_action);
11924 #endif
11925
11926     for (i = 0; i < MAX_PLAYERS; i++)
11927       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11928
11929     for (i = 0; i < MAX_PLAYERS; i++)
11930       stored_player[i].effective_action = mapped_action[i];
11931
11932 #if DEBUG_PLAYER_ACTIONS
11933     DebugContinued("", "=> ");
11934     for (i = 0; i < MAX_PLAYERS; i++)
11935       DebugContinued("", "%d, ", stored_player[i].effective_action);
11936     DebugContinued("game:playing:player", "\n");
11937 #endif
11938   }
11939 #if DEBUG_PLAYER_ACTIONS
11940   else
11941   {
11942     for (i = 0; i < MAX_PLAYERS; i++)
11943       DebugContinued("", "%d, ", stored_player[i].effective_action);
11944     DebugContinued("game:playing:player", "\n");
11945   }
11946 #endif
11947 #endif
11948
11949   for (i = 0; i < MAX_PLAYERS; i++)
11950   {
11951     // allow engine snapshot in case of changed movement attempt
11952     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11953         (stored_player[i].effective_action & KEY_MOTION))
11954       game.snapshot.changed_action = TRUE;
11955
11956     // allow engine snapshot in case of snapping/dropping attempt
11957     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11958         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11959       game.snapshot.changed_action = TRUE;
11960
11961     game.snapshot.last_action[i] = stored_player[i].effective_action;
11962   }
11963
11964   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11965   {
11966     GameActions_EM_Main();
11967   }
11968   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11969   {
11970     GameActions_SP_Main();
11971   }
11972   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11973   {
11974     GameActions_MM_Main();
11975   }
11976   else
11977   {
11978     GameActions_RND_Main();
11979   }
11980
11981   BlitScreenToBitmap(backbuffer);
11982
11983   CheckLevelSolved();
11984   CheckLevelTime();
11985
11986   AdvanceFrameAndPlayerCounters(-1);    // advance counters for all players
11987
11988   if (global.show_frames_per_second)
11989   {
11990     static unsigned int fps_counter = 0;
11991     static int fps_frames = 0;
11992     unsigned int fps_delay_ms = Counter() - fps_counter;
11993
11994     fps_frames++;
11995
11996     if (fps_delay_ms >= 500)    // calculate FPS every 0.5 seconds
11997     {
11998       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11999
12000       fps_frames = 0;
12001       fps_counter = Counter();
12002
12003       // always draw FPS to screen after FPS value was updated
12004       redraw_mask |= REDRAW_FPS;
12005     }
12006
12007     // only draw FPS if no screen areas are deactivated (invisible warp mode)
12008     if (GetDrawDeactivationMask() == REDRAW_NONE)
12009       redraw_mask |= REDRAW_FPS;
12010   }
12011 }
12012
12013 static void GameActions_CheckSaveEngineSnapshot(void)
12014 {
12015   if (!game.snapshot.save_snapshot)
12016     return;
12017
12018   // clear flag for saving snapshot _before_ saving snapshot
12019   game.snapshot.save_snapshot = FALSE;
12020
12021   SaveEngineSnapshotToList();
12022 }
12023
12024 void GameActions(void)
12025 {
12026   GameActionsExt();
12027
12028   GameActions_CheckSaveEngineSnapshot();
12029 }
12030
12031 void GameActions_EM_Main(void)
12032 {
12033   byte effective_action[MAX_PLAYERS];
12034   int i;
12035
12036   for (i = 0; i < MAX_PLAYERS; i++)
12037     effective_action[i] = stored_player[i].effective_action;
12038
12039   GameActions_EM(effective_action);
12040 }
12041
12042 void GameActions_SP_Main(void)
12043 {
12044   byte effective_action[MAX_PLAYERS];
12045   int i;
12046
12047   for (i = 0; i < MAX_PLAYERS; i++)
12048     effective_action[i] = stored_player[i].effective_action;
12049
12050   GameActions_SP(effective_action);
12051
12052   for (i = 0; i < MAX_PLAYERS; i++)
12053   {
12054     if (stored_player[i].force_dropping)
12055       stored_player[i].action |= KEY_BUTTON_DROP;
12056
12057     stored_player[i].force_dropping = FALSE;
12058   }
12059 }
12060
12061 void GameActions_MM_Main(void)
12062 {
12063   AdvanceGfxFrame();
12064
12065   GameActions_MM(local_player->effective_mouse_action);
12066 }
12067
12068 void GameActions_RND_Main(void)
12069 {
12070   GameActions_RND();
12071 }
12072
12073 void GameActions_RND(void)
12074 {
12075   static struct MouseActionInfo mouse_action_last = { 0 };
12076   struct MouseActionInfo mouse_action = local_player->effective_mouse_action;
12077   int magic_wall_x = 0, magic_wall_y = 0;
12078   int i, x, y, element, graphic, last_gfx_frame;
12079
12080   InitPlayfieldScanModeVars();
12081
12082   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12083   {
12084     SCAN_PLAYFIELD(x, y)
12085     {
12086       ChangeCount[x][y] = 0;
12087       ChangeEvent[x][y] = -1;
12088     }
12089   }
12090
12091   if (game.set_centered_player)
12092   {
12093     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12094
12095     // switching to "all players" only possible if all players fit to screen
12096     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12097     {
12098       game.centered_player_nr_next = game.centered_player_nr;
12099       game.set_centered_player = FALSE;
12100     }
12101
12102     // do not switch focus to non-existing (or non-active) player
12103     if (game.centered_player_nr_next >= 0 &&
12104         !stored_player[game.centered_player_nr_next].active)
12105     {
12106       game.centered_player_nr_next = game.centered_player_nr;
12107       game.set_centered_player = FALSE;
12108     }
12109   }
12110
12111   if (game.set_centered_player &&
12112       ScreenMovPos == 0)        // screen currently aligned at tile position
12113   {
12114     int sx, sy;
12115
12116     if (game.centered_player_nr_next == -1)
12117     {
12118       setScreenCenteredToAllPlayers(&sx, &sy);
12119     }
12120     else
12121     {
12122       sx = stored_player[game.centered_player_nr_next].jx;
12123       sy = stored_player[game.centered_player_nr_next].jy;
12124     }
12125
12126     game.centered_player_nr = game.centered_player_nr_next;
12127     game.set_centered_player = FALSE;
12128
12129     DrawRelocateScreen(0, 0, sx, sy, TRUE, setup.quick_switch);
12130     DrawGameDoorValues();
12131   }
12132
12133   // check single step mode (set flag and clear again if any player is active)
12134   game.enter_single_step_mode =
12135     (tape.single_step && tape.recording && !tape.pausing);
12136
12137   for (i = 0; i < MAX_PLAYERS; i++)
12138   {
12139     int actual_player_action = stored_player[i].effective_action;
12140
12141 #if 1
12142     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12143        - rnd_equinox_tetrachloride 048
12144        - rnd_equinox_tetrachloride_ii 096
12145        - rnd_emanuel_schmieg 002
12146        - doctor_sloan_ww 001, 020
12147     */
12148     if (stored_player[i].MovPos == 0)
12149       CheckGravityMovement(&stored_player[i]);
12150 #endif
12151
12152     // overwrite programmed action with tape action
12153     if (stored_player[i].programmed_action)
12154       actual_player_action = stored_player[i].programmed_action;
12155
12156     PlayerActions(&stored_player[i], actual_player_action);
12157
12158     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12159   }
12160
12161   // single step pause mode may already have been toggled by "ScrollPlayer()"
12162   if (game.enter_single_step_mode && !tape.pausing)
12163     TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12164
12165   ScrollScreen(NULL, SCROLL_GO_ON);
12166
12167   /* for backwards compatibility, the following code emulates a fixed bug that
12168      occured when pushing elements (causing elements that just made their last
12169      pushing step to already (if possible) make their first falling step in the
12170      same game frame, which is bad); this code is also needed to use the famous
12171      "spring push bug" which is used in older levels and might be wanted to be
12172      used also in newer levels, but in this case the buggy pushing code is only
12173      affecting the "spring" element and no other elements */
12174
12175   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12176   {
12177     for (i = 0; i < MAX_PLAYERS; i++)
12178     {
12179       struct PlayerInfo *player = &stored_player[i];
12180       int x = player->jx;
12181       int y = player->jy;
12182
12183       if (player->active && player->is_pushing && player->is_moving &&
12184           IS_MOVING(x, y) &&
12185           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12186            Tile[x][y] == EL_SPRING))
12187       {
12188         ContinueMoving(x, y);
12189
12190         // continue moving after pushing (this is actually a bug)
12191         if (!IS_MOVING(x, y))
12192           Stop[x][y] = FALSE;
12193       }
12194     }
12195   }
12196
12197   SCAN_PLAYFIELD(x, y)
12198   {
12199     Last[x][y] = Tile[x][y];
12200
12201     ChangeCount[x][y] = 0;
12202     ChangeEvent[x][y] = -1;
12203
12204     // this must be handled before main playfield loop
12205     if (Tile[x][y] == EL_PLAYER_IS_LEAVING)
12206     {
12207       MovDelay[x][y]--;
12208       if (MovDelay[x][y] <= 0)
12209         RemoveField(x, y);
12210     }
12211
12212     if (Tile[x][y] == EL_ELEMENT_SNAPPING)
12213     {
12214       MovDelay[x][y]--;
12215       if (MovDelay[x][y] <= 0)
12216       {
12217         int element = Store[x][y];
12218         int move_direction = MovDir[x][y];
12219         int player_index_bit = Store2[x][y];
12220
12221         Store[x][y] = 0;
12222         Store2[x][y] = 0;
12223
12224         RemoveField(x, y);
12225         TEST_DrawLevelField(x, y);
12226
12227         TestFieldAfterSnapping(x, y, element, move_direction, player_index_bit);
12228
12229         if (IS_ENVELOPE(element))
12230           local_player->show_envelope = element;
12231       }
12232     }
12233
12234 #if DEBUG
12235     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12236     {
12237       Debug("game:playing:GameActions_RND", "x = %d, y = %d: ChangePage != -1",
12238             x, y);
12239       Debug("game:playing:GameActions_RND", "This should never happen!");
12240
12241       ChangePage[x][y] = -1;
12242     }
12243 #endif
12244
12245     Stop[x][y] = FALSE;
12246     if (WasJustMoving[x][y] > 0)
12247       WasJustMoving[x][y]--;
12248     if (WasJustFalling[x][y] > 0)
12249       WasJustFalling[x][y]--;
12250     if (CheckCollision[x][y] > 0)
12251       CheckCollision[x][y]--;
12252     if (CheckImpact[x][y] > 0)
12253       CheckImpact[x][y]--;
12254
12255     GfxFrame[x][y]++;
12256
12257     /* reset finished pushing action (not done in ContinueMoving() to allow
12258        continuous pushing animation for elements with zero push delay) */
12259     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12260     {
12261       ResetGfxAnimation(x, y);
12262       TEST_DrawLevelField(x, y);
12263     }
12264
12265 #if DEBUG
12266     if (IS_BLOCKED(x, y))
12267     {
12268       int oldx, oldy;
12269
12270       Blocked2Moving(x, y, &oldx, &oldy);
12271       if (!IS_MOVING(oldx, oldy))
12272       {
12273         Debug("game:playing:GameActions_RND", "(BLOCKED => MOVING) context corrupted!");
12274         Debug("game:playing:GameActions_RND", "BLOCKED: x = %d, y = %d", x, y);
12275         Debug("game:playing:GameActions_RND", "!MOVING: oldx = %d, oldy = %d", oldx, oldy);
12276         Debug("game:playing:GameActions_RND", "This should never happen!");
12277       }
12278     }
12279 #endif
12280   }
12281
12282   if (mouse_action.button)
12283   {
12284     int new_button = (mouse_action.button && mouse_action_last.button == 0);
12285     int ch_button = CH_SIDE_FROM_BUTTON(mouse_action.button);
12286
12287     x = mouse_action.lx;
12288     y = mouse_action.ly;
12289     element = Tile[x][y];
12290
12291     if (new_button)
12292     {
12293       CheckElementChangeByMouse(x, y, element, CE_CLICKED_BY_MOUSE, ch_button);
12294       CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_CLICKED_ON_X,
12295                                          ch_button);
12296     }
12297
12298     CheckElementChangeByMouse(x, y, element, CE_PRESSED_BY_MOUSE, ch_button);
12299     CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_PRESSED_ON_X,
12300                                        ch_button);
12301
12302     if (level.use_step_counter)
12303     {
12304       boolean counted_click = FALSE;
12305
12306       // element clicked that can change when clicked/pressed
12307       if (CAN_CHANGE_OR_HAS_ACTION(element) &&
12308           (HAS_ANY_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
12309            HAS_ANY_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE)))
12310         counted_click = TRUE;
12311
12312       // element clicked that can trigger change when clicked/pressed
12313       if (trigger_events[element][CE_MOUSE_CLICKED_ON_X] ||
12314           trigger_events[element][CE_MOUSE_PRESSED_ON_X])
12315         counted_click = TRUE;
12316
12317       if (new_button && counted_click)
12318         CheckLevelTime_StepCounter();
12319     }
12320   }
12321
12322   SCAN_PLAYFIELD(x, y)
12323   {
12324     element = Tile[x][y];
12325     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12326     last_gfx_frame = GfxFrame[x][y];
12327
12328     if (element == EL_EMPTY)
12329       graphic = el2img(GfxElementEmpty[x][y]);
12330
12331     ResetGfxFrame(x, y);
12332
12333     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
12334       DrawLevelGraphicAnimation(x, y, graphic);
12335
12336     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12337         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12338       ResetRandomAnimationValue(x, y);
12339
12340     SetRandomAnimationValue(x, y);
12341
12342     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12343
12344     if (IS_INACTIVE(element))
12345     {
12346       if (IS_ANIMATED(graphic))
12347         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12348
12349       continue;
12350     }
12351
12352     // this may take place after moving, so 'element' may have changed
12353     if (IS_CHANGING(x, y) &&
12354         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12355     {
12356       int page = element_info[element].event_page_nr[CE_DELAY];
12357
12358       HandleElementChange(x, y, page);
12359
12360       element = Tile[x][y];
12361       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12362     }
12363
12364     CheckNextToConditions(x, y);
12365
12366     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12367     {
12368       StartMoving(x, y);
12369
12370       element = Tile[x][y];
12371       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12372
12373       if (IS_ANIMATED(graphic) &&
12374           !IS_MOVING(x, y) &&
12375           !Stop[x][y])
12376         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12377
12378       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12379         TEST_DrawTwinkleOnField(x, y);
12380     }
12381     else if (element == EL_ACID)
12382     {
12383       if (!Stop[x][y])
12384         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12385     }
12386     else if ((element == EL_EXIT_OPEN ||
12387               element == EL_EM_EXIT_OPEN ||
12388               element == EL_SP_EXIT_OPEN ||
12389               element == EL_STEEL_EXIT_OPEN ||
12390               element == EL_EM_STEEL_EXIT_OPEN ||
12391               element == EL_SP_TERMINAL ||
12392               element == EL_SP_TERMINAL_ACTIVE ||
12393               element == EL_EXTRA_TIME ||
12394               element == EL_SHIELD_NORMAL ||
12395               element == EL_SHIELD_DEADLY) &&
12396              IS_ANIMATED(graphic))
12397       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12398     else if (IS_MOVING(x, y))
12399       ContinueMoving(x, y);
12400     else if (IS_ACTIVE_BOMB(element))
12401       CheckDynamite(x, y);
12402     else if (element == EL_AMOEBA_GROWING)
12403       AmoebaGrowing(x, y);
12404     else if (element == EL_AMOEBA_SHRINKING)
12405       AmoebaShrinking(x, y);
12406
12407 #if !USE_NEW_AMOEBA_CODE
12408     else if (IS_AMOEBALIVE(element))
12409       AmoebaReproduce(x, y);
12410 #endif
12411
12412     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12413       Life(x, y);
12414     else if (element == EL_EXIT_CLOSED)
12415       CheckExit(x, y);
12416     else if (element == EL_EM_EXIT_CLOSED)
12417       CheckExitEM(x, y);
12418     else if (element == EL_STEEL_EXIT_CLOSED)
12419       CheckExitSteel(x, y);
12420     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12421       CheckExitSteelEM(x, y);
12422     else if (element == EL_SP_EXIT_CLOSED)
12423       CheckExitSP(x, y);
12424     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12425              element == EL_EXPANDABLE_STEELWALL_GROWING)
12426       WallGrowing(x, y);
12427     else if (element == EL_EXPANDABLE_WALL ||
12428              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12429              element == EL_EXPANDABLE_WALL_VERTICAL ||
12430              element == EL_EXPANDABLE_WALL_ANY ||
12431              element == EL_BD_EXPANDABLE_WALL ||
12432              element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12433              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12434              element == EL_EXPANDABLE_STEELWALL_ANY)
12435       CheckWallGrowing(x, y);
12436     else if (element == EL_FLAMES)
12437       CheckForDragon(x, y);
12438     else if (element == EL_EXPLOSION)
12439       ; // drawing of correct explosion animation is handled separately
12440     else if (element == EL_ELEMENT_SNAPPING ||
12441              element == EL_DIAGONAL_SHRINKING ||
12442              element == EL_DIAGONAL_GROWING)
12443     {
12444       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y], GfxDir[x][y]);
12445
12446       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12447     }
12448     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12449       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12450
12451     if (IS_BELT_ACTIVE(element))
12452       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12453
12454     if (game.magic_wall_active)
12455     {
12456       int jx = local_player->jx, jy = local_player->jy;
12457
12458       // play the element sound at the position nearest to the player
12459       if ((element == EL_MAGIC_WALL_FULL ||
12460            element == EL_MAGIC_WALL_ACTIVE ||
12461            element == EL_MAGIC_WALL_EMPTYING ||
12462            element == EL_BD_MAGIC_WALL_FULL ||
12463            element == EL_BD_MAGIC_WALL_ACTIVE ||
12464            element == EL_BD_MAGIC_WALL_EMPTYING ||
12465            element == EL_DC_MAGIC_WALL_FULL ||
12466            element == EL_DC_MAGIC_WALL_ACTIVE ||
12467            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12468           ABS(x - jx) + ABS(y - jy) <
12469           ABS(magic_wall_x - jx) + ABS(magic_wall_y - jy))
12470       {
12471         magic_wall_x = x;
12472         magic_wall_y = y;
12473       }
12474     }
12475   }
12476
12477 #if USE_NEW_AMOEBA_CODE
12478   // new experimental amoeba growth stuff
12479   if (!(FrameCounter % 8))
12480   {
12481     static unsigned int random = 1684108901;
12482
12483     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12484     {
12485       x = RND(lev_fieldx);
12486       y = RND(lev_fieldy);
12487       element = Tile[x][y];
12488
12489       if (!IS_PLAYER(x, y) &&
12490           (element == EL_EMPTY ||
12491            CAN_GROW_INTO(element) ||
12492            element == EL_QUICKSAND_EMPTY ||
12493            element == EL_QUICKSAND_FAST_EMPTY ||
12494            element == EL_ACID_SPLASH_LEFT ||
12495            element == EL_ACID_SPLASH_RIGHT))
12496       {
12497         if ((IN_LEV_FIELD(x, y - 1) && Tile[x][y - 1] == EL_AMOEBA_WET) ||
12498             (IN_LEV_FIELD(x - 1, y) && Tile[x - 1][y] == EL_AMOEBA_WET) ||
12499             (IN_LEV_FIELD(x + 1, y) && Tile[x + 1][y] == EL_AMOEBA_WET) ||
12500             (IN_LEV_FIELD(x, y + 1) && Tile[x][y + 1] == EL_AMOEBA_WET))
12501           Tile[x][y] = EL_AMOEBA_DROP;
12502       }
12503
12504       random = random * 129 + 1;
12505     }
12506   }
12507 #endif
12508
12509   game.explosions_delayed = FALSE;
12510
12511   SCAN_PLAYFIELD(x, y)
12512   {
12513     element = Tile[x][y];
12514
12515     if (ExplodeField[x][y])
12516       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12517     else if (element == EL_EXPLOSION)
12518       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12519
12520     ExplodeField[x][y] = EX_TYPE_NONE;
12521   }
12522
12523   game.explosions_delayed = TRUE;
12524
12525   if (game.magic_wall_active)
12526   {
12527     if (!(game.magic_wall_time_left % 4))
12528     {
12529       int element = Tile[magic_wall_x][magic_wall_y];
12530
12531       if (element == EL_BD_MAGIC_WALL_FULL ||
12532           element == EL_BD_MAGIC_WALL_ACTIVE ||
12533           element == EL_BD_MAGIC_WALL_EMPTYING)
12534         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12535       else if (element == EL_DC_MAGIC_WALL_FULL ||
12536                element == EL_DC_MAGIC_WALL_ACTIVE ||
12537                element == EL_DC_MAGIC_WALL_EMPTYING)
12538         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12539       else
12540         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12541     }
12542
12543     if (game.magic_wall_time_left > 0)
12544     {
12545       game.magic_wall_time_left--;
12546
12547       if (!game.magic_wall_time_left)
12548       {
12549         SCAN_PLAYFIELD(x, y)
12550         {
12551           element = Tile[x][y];
12552
12553           if (element == EL_MAGIC_WALL_ACTIVE ||
12554               element == EL_MAGIC_WALL_FULL)
12555           {
12556             Tile[x][y] = EL_MAGIC_WALL_DEAD;
12557             TEST_DrawLevelField(x, y);
12558           }
12559           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12560                    element == EL_BD_MAGIC_WALL_FULL)
12561           {
12562             Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
12563             TEST_DrawLevelField(x, y);
12564           }
12565           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12566                    element == EL_DC_MAGIC_WALL_FULL)
12567           {
12568             Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
12569             TEST_DrawLevelField(x, y);
12570           }
12571         }
12572
12573         game.magic_wall_active = FALSE;
12574       }
12575     }
12576   }
12577
12578   if (game.light_time_left > 0)
12579   {
12580     game.light_time_left--;
12581
12582     if (game.light_time_left == 0)
12583       RedrawAllLightSwitchesAndInvisibleElements();
12584   }
12585
12586   if (game.timegate_time_left > 0)
12587   {
12588     game.timegate_time_left--;
12589
12590     if (game.timegate_time_left == 0)
12591       CloseAllOpenTimegates();
12592   }
12593
12594   if (game.lenses_time_left > 0)
12595   {
12596     game.lenses_time_left--;
12597
12598     if (game.lenses_time_left == 0)
12599       RedrawAllInvisibleElementsForLenses();
12600   }
12601
12602   if (game.magnify_time_left > 0)
12603   {
12604     game.magnify_time_left--;
12605
12606     if (game.magnify_time_left == 0)
12607       RedrawAllInvisibleElementsForMagnifier();
12608   }
12609
12610   for (i = 0; i < MAX_PLAYERS; i++)
12611   {
12612     struct PlayerInfo *player = &stored_player[i];
12613
12614     if (SHIELD_ON(player))
12615     {
12616       if (player->shield_deadly_time_left)
12617         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12618       else if (player->shield_normal_time_left)
12619         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12620     }
12621   }
12622
12623 #if USE_DELAYED_GFX_REDRAW
12624   SCAN_PLAYFIELD(x, y)
12625   {
12626     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12627     {
12628       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12629          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12630
12631       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12632         DrawLevelField(x, y);
12633
12634       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12635         DrawLevelFieldCrumbled(x, y);
12636
12637       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12638         DrawLevelFieldCrumbledNeighbours(x, y);
12639
12640       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12641         DrawTwinkleOnField(x, y);
12642     }
12643
12644     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12645   }
12646 #endif
12647
12648   DrawAllPlayers();
12649   PlayAllPlayersSound();
12650
12651   for (i = 0; i < MAX_PLAYERS; i++)
12652   {
12653     struct PlayerInfo *player = &stored_player[i];
12654
12655     if (player->show_envelope != 0 && (!player->active ||
12656                                        player->MovPos == 0))
12657     {
12658       ShowEnvelope(player->show_envelope - EL_ENVELOPE_1);
12659
12660       player->show_envelope = 0;
12661     }
12662   }
12663
12664   // use random number generator in every frame to make it less predictable
12665   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12666     RND(1);
12667
12668   mouse_action_last = mouse_action;
12669 }
12670
12671 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12672 {
12673   int min_x = x, min_y = y, max_x = x, max_y = y;
12674   int scr_fieldx = getScreenFieldSizeX();
12675   int scr_fieldy = getScreenFieldSizeY();
12676   int i;
12677
12678   for (i = 0; i < MAX_PLAYERS; i++)
12679   {
12680     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12681
12682     if (!stored_player[i].active || &stored_player[i] == player)
12683       continue;
12684
12685     min_x = MIN(min_x, jx);
12686     min_y = MIN(min_y, jy);
12687     max_x = MAX(max_x, jx);
12688     max_y = MAX(max_y, jy);
12689   }
12690
12691   return (max_x - min_x < scr_fieldx && max_y - min_y < scr_fieldy);
12692 }
12693
12694 static boolean AllPlayersInVisibleScreen(void)
12695 {
12696   int i;
12697
12698   for (i = 0; i < MAX_PLAYERS; i++)
12699   {
12700     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12701
12702     if (!stored_player[i].active)
12703       continue;
12704
12705     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12706       return FALSE;
12707   }
12708
12709   return TRUE;
12710 }
12711
12712 void ScrollLevel(int dx, int dy)
12713 {
12714   int scroll_offset = 2 * TILEX_VAR;
12715   int x, y;
12716
12717   BlitBitmap(drawto_field, drawto_field,
12718              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12719              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12720              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12721              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12722              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12723              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12724
12725   if (dx != 0)
12726   {
12727     x = (dx == 1 ? BX1 : BX2);
12728     for (y = BY1; y <= BY2; y++)
12729       DrawScreenField(x, y);
12730   }
12731
12732   if (dy != 0)
12733   {
12734     y = (dy == 1 ? BY1 : BY2);
12735     for (x = BX1; x <= BX2; x++)
12736       DrawScreenField(x, y);
12737   }
12738
12739   redraw_mask |= REDRAW_FIELD;
12740 }
12741
12742 static boolean canFallDown(struct PlayerInfo *player)
12743 {
12744   int jx = player->jx, jy = player->jy;
12745
12746   return (IN_LEV_FIELD(jx, jy + 1) &&
12747           (IS_FREE(jx, jy + 1) ||
12748            (Tile[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12749           IS_WALKABLE_FROM(Tile[jx][jy], MV_DOWN) &&
12750           !IS_WALKABLE_INSIDE(Tile[jx][jy]));
12751 }
12752
12753 static boolean canPassField(int x, int y, int move_dir)
12754 {
12755   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12756   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12757   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12758   int nextx = x + dx;
12759   int nexty = y + dy;
12760   int element = Tile[x][y];
12761
12762   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12763           !CAN_MOVE(element) &&
12764           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12765           IS_WALKABLE_FROM(Tile[nextx][nexty], move_dir) &&
12766           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12767 }
12768
12769 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12770 {
12771   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12772   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12773   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12774   int newx = x + dx;
12775   int newy = y + dy;
12776
12777   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12778           IS_GRAVITY_REACHABLE(Tile[newx][newy]) &&
12779           (IS_DIGGABLE(Tile[newx][newy]) ||
12780            IS_WALKABLE_FROM(Tile[newx][newy], opposite_dir) ||
12781            canPassField(newx, newy, move_dir)));
12782 }
12783
12784 static void CheckGravityMovement(struct PlayerInfo *player)
12785 {
12786   if (player->gravity && !player->programmed_action)
12787   {
12788     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12789     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12790     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12791     int jx = player->jx, jy = player->jy;
12792     boolean player_is_moving_to_valid_field =
12793       (!player_is_snapping &&
12794        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12795         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12796     boolean player_can_fall_down = canFallDown(player);
12797
12798     if (player_can_fall_down &&
12799         !player_is_moving_to_valid_field)
12800       player->programmed_action = MV_DOWN;
12801   }
12802 }
12803
12804 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12805 {
12806   return CheckGravityMovement(player);
12807
12808   if (player->gravity && !player->programmed_action)
12809   {
12810     int jx = player->jx, jy = player->jy;
12811     boolean field_under_player_is_free =
12812       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12813     boolean player_is_standing_on_valid_field =
12814       (IS_WALKABLE_INSIDE(Tile[jx][jy]) ||
12815        (IS_WALKABLE(Tile[jx][jy]) &&
12816         !(element_info[Tile[jx][jy]].access_direction & MV_DOWN)));
12817
12818     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12819       player->programmed_action = MV_DOWN;
12820   }
12821 }
12822
12823 /*
12824   MovePlayerOneStep()
12825   -----------------------------------------------------------------------------
12826   dx, dy:               direction (non-diagonal) to try to move the player to
12827   real_dx, real_dy:     direction as read from input device (can be diagonal)
12828 */
12829
12830 boolean MovePlayerOneStep(struct PlayerInfo *player,
12831                           int dx, int dy, int real_dx, int real_dy)
12832 {
12833   int jx = player->jx, jy = player->jy;
12834   int new_jx = jx + dx, new_jy = jy + dy;
12835   int can_move;
12836   boolean player_can_move = !player->cannot_move;
12837
12838   if (!player->active || (!dx && !dy))
12839     return MP_NO_ACTION;
12840
12841   player->MovDir = (dx < 0 ? MV_LEFT :
12842                     dx > 0 ? MV_RIGHT :
12843                     dy < 0 ? MV_UP :
12844                     dy > 0 ? MV_DOWN :  MV_NONE);
12845
12846   if (!IN_LEV_FIELD(new_jx, new_jy))
12847     return MP_NO_ACTION;
12848
12849   if (!player_can_move)
12850   {
12851     if (player->MovPos == 0)
12852     {
12853       player->is_moving = FALSE;
12854       player->is_digging = FALSE;
12855       player->is_collecting = FALSE;
12856       player->is_snapping = FALSE;
12857       player->is_pushing = FALSE;
12858     }
12859   }
12860
12861   if (!network.enabled && game.centered_player_nr == -1 &&
12862       !AllPlayersInSight(player, new_jx, new_jy))
12863     return MP_NO_ACTION;
12864
12865   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx, real_dy, DF_DIG);
12866   if (can_move != MP_MOVING)
12867     return can_move;
12868
12869   // check if DigField() has caused relocation of the player
12870   if (player->jx != jx || player->jy != jy)
12871     return MP_NO_ACTION;        // <-- !!! CHECK THIS [-> MP_ACTION ?] !!!
12872
12873   StorePlayer[jx][jy] = 0;
12874   player->last_jx = jx;
12875   player->last_jy = jy;
12876   player->jx = new_jx;
12877   player->jy = new_jy;
12878   StorePlayer[new_jx][new_jy] = player->element_nr;
12879
12880   if (player->move_delay_value_next != -1)
12881   {
12882     player->move_delay_value = player->move_delay_value_next;
12883     player->move_delay_value_next = -1;
12884   }
12885
12886   player->MovPos =
12887     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12888
12889   player->step_counter++;
12890
12891   PlayerVisit[jx][jy] = FrameCounter;
12892
12893   player->is_moving = TRUE;
12894
12895 #if 1
12896   // should better be called in MovePlayer(), but this breaks some tapes
12897   ScrollPlayer(player, SCROLL_INIT);
12898 #endif
12899
12900   return MP_MOVING;
12901 }
12902
12903 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12904 {
12905   int jx = player->jx, jy = player->jy;
12906   int old_jx = jx, old_jy = jy;
12907   int moved = MP_NO_ACTION;
12908
12909   if (!player->active)
12910     return FALSE;
12911
12912   if (!dx && !dy)
12913   {
12914     if (player->MovPos == 0)
12915     {
12916       player->is_moving = FALSE;
12917       player->is_digging = FALSE;
12918       player->is_collecting = FALSE;
12919       player->is_snapping = FALSE;
12920       player->is_pushing = FALSE;
12921     }
12922
12923     return FALSE;
12924   }
12925
12926   if (player->move_delay > 0)
12927     return FALSE;
12928
12929   player->move_delay = -1;              // set to "uninitialized" value
12930
12931   // store if player is automatically moved to next field
12932   player->is_auto_moving = (player->programmed_action != MV_NONE);
12933
12934   // remove the last programmed player action
12935   player->programmed_action = 0;
12936
12937   if (player->MovPos)
12938   {
12939     // should only happen if pre-1.2 tape recordings are played
12940     // this is only for backward compatibility
12941
12942     int original_move_delay_value = player->move_delay_value;
12943
12944 #if DEBUG
12945     Debug("game:playing:MovePlayer",
12946           "THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]",
12947           tape.counter);
12948 #endif
12949
12950     // scroll remaining steps with finest movement resolution
12951     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12952
12953     while (player->MovPos)
12954     {
12955       ScrollPlayer(player, SCROLL_GO_ON);
12956       ScrollScreen(NULL, SCROLL_GO_ON);
12957
12958       AdvanceFrameAndPlayerCounters(player->index_nr);
12959
12960       DrawAllPlayers();
12961       BackToFront_WithFrameDelay(0);
12962     }
12963
12964     player->move_delay_value = original_move_delay_value;
12965   }
12966
12967   player->is_active = FALSE;
12968
12969   if (player->last_move_dir & MV_HORIZONTAL)
12970   {
12971     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12972       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12973   }
12974   else
12975   {
12976     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12977       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12978   }
12979
12980   if (!moved && !player->is_active)
12981   {
12982     player->is_moving = FALSE;
12983     player->is_digging = FALSE;
12984     player->is_collecting = FALSE;
12985     player->is_snapping = FALSE;
12986     player->is_pushing = FALSE;
12987   }
12988
12989   jx = player->jx;
12990   jy = player->jy;
12991
12992   if (moved & MP_MOVING && !ScreenMovPos &&
12993       (player->index_nr == game.centered_player_nr ||
12994        game.centered_player_nr == -1))
12995   {
12996     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12997
12998     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12999     {
13000       // actual player has left the screen -- scroll in that direction
13001       if (jx != old_jx)         // player has moved horizontally
13002         scroll_x += (jx - old_jx);
13003       else                      // player has moved vertically
13004         scroll_y += (jy - old_jy);
13005     }
13006     else
13007     {
13008       int offset_raw = game.scroll_delay_value;
13009
13010       if (jx != old_jx)         // player has moved horizontally
13011       {
13012         int offset = MIN(offset_raw, (SCR_FIELDX - 2) / 2);
13013         int offset_x = offset * (player->MovDir == MV_LEFT ? +1 : -1);
13014         int new_scroll_x = jx - MIDPOSX + offset_x;
13015
13016         if ((player->MovDir == MV_LEFT  && scroll_x > new_scroll_x) ||
13017             (player->MovDir == MV_RIGHT && scroll_x < new_scroll_x))
13018           scroll_x = new_scroll_x;
13019
13020         // don't scroll over playfield boundaries
13021         scroll_x = MIN(MAX(SBX_Left, scroll_x), SBX_Right);
13022
13023         // don't scroll more than one field at a time
13024         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13025
13026         // don't scroll against the player's moving direction
13027         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
13028             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13029           scroll_x = old_scroll_x;
13030       }
13031       else                      // player has moved vertically
13032       {
13033         int offset = MIN(offset_raw, (SCR_FIELDY - 2) / 2);
13034         int offset_y = offset * (player->MovDir == MV_UP ? +1 : -1);
13035         int new_scroll_y = jy - MIDPOSY + offset_y;
13036
13037         if ((player->MovDir == MV_UP   && scroll_y > new_scroll_y) ||
13038             (player->MovDir == MV_DOWN && scroll_y < new_scroll_y))
13039           scroll_y = new_scroll_y;
13040
13041         // don't scroll over playfield boundaries
13042         scroll_y = MIN(MAX(SBY_Upper, scroll_y), SBY_Lower);
13043
13044         // don't scroll more than one field at a time
13045         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13046
13047         // don't scroll against the player's moving direction
13048         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
13049             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13050           scroll_y = old_scroll_y;
13051       }
13052     }
13053
13054     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13055     {
13056       if (!network.enabled && game.centered_player_nr == -1 &&
13057           !AllPlayersInVisibleScreen())
13058       {
13059         scroll_x = old_scroll_x;
13060         scroll_y = old_scroll_y;
13061       }
13062       else
13063       {
13064         ScrollScreen(player, SCROLL_INIT);
13065         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13066       }
13067     }
13068   }
13069
13070   player->StepFrame = 0;
13071
13072   if (moved & MP_MOVING)
13073   {
13074     if (old_jx != jx && old_jy == jy)
13075       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13076     else if (old_jx == jx && old_jy != jy)
13077       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13078
13079     TEST_DrawLevelField(jx, jy);        // for "crumbled sand"
13080
13081     player->last_move_dir = player->MovDir;
13082     player->is_moving = TRUE;
13083     player->is_snapping = FALSE;
13084     player->is_switching = FALSE;
13085     player->is_dropping = FALSE;
13086     player->is_dropping_pressed = FALSE;
13087     player->drop_pressed_delay = 0;
13088
13089 #if 0
13090     // should better be called here than above, but this breaks some tapes
13091     ScrollPlayer(player, SCROLL_INIT);
13092 #endif
13093   }
13094   else
13095   {
13096     CheckGravityMovementWhenNotMoving(player);
13097
13098     player->is_moving = FALSE;
13099
13100     /* at this point, the player is allowed to move, but cannot move right now
13101        (e.g. because of something blocking the way) -- ensure that the player
13102        is also allowed to move in the next frame (in old versions before 3.1.1,
13103        the player was forced to wait again for eight frames before next try) */
13104
13105     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13106       player->move_delay = 0;   // allow direct movement in the next frame
13107   }
13108
13109   if (player->move_delay == -1)         // not yet initialized by DigField()
13110     player->move_delay = player->move_delay_value;
13111
13112   if (game.engine_version < VERSION_IDENT(3,0,7,0))
13113   {
13114     TestIfPlayerTouchesBadThing(jx, jy);
13115     TestIfPlayerTouchesCustomElement(jx, jy);
13116   }
13117
13118   if (!player->active)
13119     RemovePlayer(player);
13120
13121   return moved;
13122 }
13123
13124 void ScrollPlayer(struct PlayerInfo *player, int mode)
13125 {
13126   int jx = player->jx, jy = player->jy;
13127   int last_jx = player->last_jx, last_jy = player->last_jy;
13128   int move_stepsize = TILEX / player->move_delay_value;
13129
13130   if (!player->active)
13131     return;
13132
13133   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      // player not moving
13134     return;
13135
13136   if (mode == SCROLL_INIT)
13137   {
13138     player->actual_frame_counter.count = FrameCounter;
13139     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13140
13141     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13142         Tile[last_jx][last_jy] == EL_EMPTY)
13143     {
13144       int last_field_block_delay = 0;   // start with no blocking at all
13145       int block_delay_adjustment = player->block_delay_adjustment;
13146
13147       // if player blocks last field, add delay for exactly one move
13148       if (player->block_last_field)
13149       {
13150         last_field_block_delay += player->move_delay_value;
13151
13152         // when blocking enabled, prevent moving up despite gravity
13153         if (player->gravity && player->MovDir == MV_UP)
13154           block_delay_adjustment = -1;
13155       }
13156
13157       // add block delay adjustment (also possible when not blocking)
13158       last_field_block_delay += block_delay_adjustment;
13159
13160       Tile[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13161       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13162     }
13163
13164     if (player->MovPos != 0)    // player has not yet reached destination
13165       return;
13166   }
13167   else if (!FrameReached(&player->actual_frame_counter))
13168     return;
13169
13170   if (player->MovPos != 0)
13171   {
13172     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13173     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13174
13175     // before DrawPlayer() to draw correct player graphic for this case
13176     if (player->MovPos == 0)
13177       CheckGravityMovement(player);
13178   }
13179
13180   if (player->MovPos == 0)      // player reached destination field
13181   {
13182     if (player->move_delay_reset_counter > 0)
13183     {
13184       player->move_delay_reset_counter--;
13185
13186       if (player->move_delay_reset_counter == 0)
13187       {
13188         // continue with normal speed after quickly moving through gate
13189         HALVE_PLAYER_SPEED(player);
13190
13191         // be able to make the next move without delay
13192         player->move_delay = 0;
13193       }
13194     }
13195
13196     if (Tile[jx][jy] == EL_EXIT_OPEN ||
13197         Tile[jx][jy] == EL_EM_EXIT_OPEN ||
13198         Tile[jx][jy] == EL_EM_EXIT_OPENING ||
13199         Tile[jx][jy] == EL_STEEL_EXIT_OPEN ||
13200         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13201         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
13202         Tile[jx][jy] == EL_SP_EXIT_OPEN ||
13203         Tile[jx][jy] == EL_SP_EXIT_OPENING)     // <-- special case
13204     {
13205       ExitPlayer(player);
13206
13207       if (game.players_still_needed == 0 &&
13208           (game.friends_still_needed == 0 ||
13209            IS_SP_ELEMENT(Tile[jx][jy])))
13210         LevelSolved();
13211     }
13212
13213     player->last_jx = jx;
13214     player->last_jy = jy;
13215
13216     // this breaks one level: "machine", level 000
13217     {
13218       int move_direction = player->MovDir;
13219       int enter_side = MV_DIR_OPPOSITE(move_direction);
13220       int leave_side = move_direction;
13221       int old_jx = last_jx;
13222       int old_jy = last_jy;
13223       int old_element = Tile[old_jx][old_jy];
13224       int new_element = Tile[jx][jy];
13225
13226       if (IS_CUSTOM_ELEMENT(old_element))
13227         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13228                                    CE_LEFT_BY_PLAYER,
13229                                    player->index_bit, leave_side);
13230
13231       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13232                                           CE_PLAYER_LEAVES_X,
13233                                           player->index_bit, leave_side);
13234
13235       // needed because pushed element has not yet reached its destination,
13236       // so it would trigger a change event at its previous field location
13237       if (!player->is_pushing)
13238       {
13239         if (IS_CUSTOM_ELEMENT(new_element))
13240           CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13241                                      player->index_bit, enter_side);
13242
13243         CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13244                                             CE_PLAYER_ENTERS_X,
13245                                             player->index_bit, enter_side);
13246       }
13247
13248       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13249                                         CE_MOVE_OF_X, move_direction);
13250     }
13251
13252     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13253     {
13254       TestIfPlayerTouchesBadThing(jx, jy);
13255       TestIfPlayerTouchesCustomElement(jx, jy);
13256
13257       // needed because pushed element has not yet reached its destination,
13258       // so it would trigger a change event at its previous field location
13259       if (!player->is_pushing)
13260         TestIfElementTouchesCustomElement(jx, jy);      // for empty space
13261
13262       if (level.finish_dig_collect &&
13263           (player->is_digging || player->is_collecting))
13264       {
13265         int last_element = player->last_removed_element;
13266         int move_direction = player->MovDir;
13267         int enter_side = MV_DIR_OPPOSITE(move_direction);
13268         int change_event = (player->is_digging ? CE_PLAYER_DIGS_X :
13269                             CE_PLAYER_COLLECTS_X);
13270
13271         CheckTriggeredElementChangeByPlayer(jx, jy, last_element, change_event,
13272                                             player->index_bit, enter_side);
13273
13274         player->last_removed_element = EL_UNDEFINED;
13275       }
13276
13277       if (!player->active)
13278         RemovePlayer(player);
13279     }
13280
13281     if (level.use_step_counter)
13282       CheckLevelTime_StepCounter();
13283
13284     if (tape.single_step && tape.recording && !tape.pausing &&
13285         !player->programmed_action)
13286       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13287
13288     if (!player->programmed_action)
13289       CheckSaveEngineSnapshot(player);
13290   }
13291 }
13292
13293 void ScrollScreen(struct PlayerInfo *player, int mode)
13294 {
13295   static DelayCounter screen_frame_counter = { 0 };
13296
13297   if (mode == SCROLL_INIT)
13298   {
13299     // set scrolling step size according to actual player's moving speed
13300     ScrollStepSize = TILEX / player->move_delay_value;
13301
13302     screen_frame_counter.count = FrameCounter;
13303     screen_frame_counter.value = 1;
13304
13305     ScreenMovDir = player->MovDir;
13306     ScreenMovPos = player->MovPos;
13307     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13308     return;
13309   }
13310   else if (!FrameReached(&screen_frame_counter))
13311     return;
13312
13313   if (ScreenMovPos)
13314   {
13315     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13316     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13317     redraw_mask |= REDRAW_FIELD;
13318   }
13319   else
13320     ScreenMovDir = MV_NONE;
13321 }
13322
13323 void CheckNextToConditions(int x, int y)
13324 {
13325   int element = Tile[x][y];
13326
13327   if (IS_PLAYER(x, y))
13328     TestIfPlayerNextToCustomElement(x, y);
13329
13330   if (CAN_CHANGE_OR_HAS_ACTION(element) &&
13331       HAS_ANY_CHANGE_EVENT(element, CE_NEXT_TO_X))
13332     TestIfElementNextToCustomElement(x, y);
13333 }
13334
13335 void TestIfPlayerNextToCustomElement(int x, int y)
13336 {
13337   struct XY *xy = xy_topdown;
13338   static int trigger_sides[4][2] =
13339   {
13340     // center side       border side
13341     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13342     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13343     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13344     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13345   };
13346   int i;
13347
13348   if (!IS_PLAYER(x, y))
13349     return;
13350
13351   struct PlayerInfo *player = PLAYERINFO(x, y);
13352
13353   if (player->is_moving)
13354     return;
13355
13356   for (i = 0; i < NUM_DIRECTIONS; i++)
13357   {
13358     int xx = x + xy[i].x;
13359     int yy = y + xy[i].y;
13360     int border_side = trigger_sides[i][1];
13361     int border_element;
13362
13363     if (!IN_LEV_FIELD(xx, yy))
13364       continue;
13365
13366     if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
13367       continue;         // center and border element not connected
13368
13369     border_element = Tile[xx][yy];
13370
13371     CheckElementChangeByPlayer(xx, yy, border_element, CE_NEXT_TO_PLAYER,
13372                                player->index_bit, border_side);
13373     CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13374                                         CE_PLAYER_NEXT_TO_X,
13375                                         player->index_bit, border_side);
13376
13377     /* use player element that is initially defined in the level playfield,
13378        not the player element that corresponds to the runtime player number
13379        (example: a level that contains EL_PLAYER_3 as the only player would
13380        incorrectly give EL_PLAYER_1 for "player->element_nr") */
13381
13382     CheckElementChangeBySide(xx, yy, border_element, player->initial_element,
13383                              CE_NEXT_TO_X, border_side);
13384   }
13385 }
13386
13387 void TestIfPlayerTouchesCustomElement(int x, int y)
13388 {
13389   struct XY *xy = xy_topdown;
13390   static int trigger_sides[4][2] =
13391   {
13392     // center side       border side
13393     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13394     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13395     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13396     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13397   };
13398   static int touch_dir[4] =
13399   {
13400     MV_LEFT | MV_RIGHT,
13401     MV_UP   | MV_DOWN,
13402     MV_UP   | MV_DOWN,
13403     MV_LEFT | MV_RIGHT
13404   };
13405   int center_element = Tile[x][y];      // should always be non-moving!
13406   int i;
13407
13408   for (i = 0; i < NUM_DIRECTIONS; i++)
13409   {
13410     int xx = x + xy[i].x;
13411     int yy = y + xy[i].y;
13412     int center_side = trigger_sides[i][0];
13413     int border_side = trigger_sides[i][1];
13414     int border_element;
13415
13416     if (!IN_LEV_FIELD(xx, yy))
13417       continue;
13418
13419     if (IS_PLAYER(x, y))                // player found at center element
13420     {
13421       struct PlayerInfo *player = PLAYERINFO(x, y);
13422
13423       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13424         border_element = Tile[xx][yy];          // may be moving!
13425       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13426         border_element = Tile[xx][yy];
13427       else if (MovDir[xx][yy] & touch_dir[i])   // elements are touching
13428         border_element = MovingOrBlocked2Element(xx, yy);
13429       else
13430         continue;               // center and border element do not touch
13431
13432       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13433                                  player->index_bit, border_side);
13434       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13435                                           CE_PLAYER_TOUCHES_X,
13436                                           player->index_bit, border_side);
13437
13438       {
13439         /* use player element that is initially defined in the level playfield,
13440            not the player element that corresponds to the runtime player number
13441            (example: a level that contains EL_PLAYER_3 as the only player would
13442            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13443         int player_element = PLAYERINFO(x, y)->initial_element;
13444
13445         CheckElementChangeBySide(xx, yy, border_element, player_element,
13446                                  CE_TOUCHING_X, border_side);
13447       }
13448     }
13449     else if (IS_PLAYER(xx, yy))         // player found at border element
13450     {
13451       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13452
13453       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13454       {
13455         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13456           continue;             // center and border element do not touch
13457       }
13458
13459       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13460                                  player->index_bit, center_side);
13461       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13462                                           CE_PLAYER_TOUCHES_X,
13463                                           player->index_bit, center_side);
13464
13465       {
13466         /* use player element that is initially defined in the level playfield,
13467            not the player element that corresponds to the runtime player number
13468            (example: a level that contains EL_PLAYER_3 as the only player would
13469            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13470         int player_element = PLAYERINFO(xx, yy)->initial_element;
13471
13472         CheckElementChangeBySide(x, y, center_element, player_element,
13473                                  CE_TOUCHING_X, center_side);
13474       }
13475
13476       break;
13477     }
13478   }
13479 }
13480
13481 void TestIfElementNextToCustomElement(int x, int y)
13482 {
13483   struct XY *xy = xy_topdown;
13484   static int trigger_sides[4][2] =
13485   {
13486     // center side      border side
13487     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13488     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13489     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13490     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13491   };
13492   int center_element = Tile[x][y];      // should always be non-moving!
13493   int i;
13494
13495   if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
13496     return;
13497
13498   for (i = 0; i < NUM_DIRECTIONS; i++)
13499   {
13500     int xx = x + xy[i].x;
13501     int yy = y + xy[i].y;
13502     int border_side = trigger_sides[i][1];
13503     int border_element;
13504
13505     if (!IN_LEV_FIELD(xx, yy))
13506       continue;
13507
13508     if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
13509       continue;                 // center and border element not connected
13510
13511     border_element = Tile[xx][yy];
13512
13513     // check for change of center element (but change it only once)
13514     if (CheckElementChangeBySide(x, y, center_element, border_element,
13515                                  CE_NEXT_TO_X, border_side))
13516       break;
13517   }
13518 }
13519
13520 void TestIfElementTouchesCustomElement(int x, int y)
13521 {
13522   struct XY *xy = xy_topdown;
13523   static int trigger_sides[4][2] =
13524   {
13525     // center side      border side
13526     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13527     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13528     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13529     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13530   };
13531   static int touch_dir[4] =
13532   {
13533     MV_LEFT | MV_RIGHT,
13534     MV_UP   | MV_DOWN,
13535     MV_UP   | MV_DOWN,
13536     MV_LEFT | MV_RIGHT
13537   };
13538   boolean change_center_element = FALSE;
13539   int center_element = Tile[x][y];      // should always be non-moving!
13540   int border_element_old[NUM_DIRECTIONS];
13541   int i;
13542
13543   for (i = 0; i < NUM_DIRECTIONS; i++)
13544   {
13545     int xx = x + xy[i].x;
13546     int yy = y + xy[i].y;
13547     int border_element;
13548
13549     border_element_old[i] = -1;
13550
13551     if (!IN_LEV_FIELD(xx, yy))
13552       continue;
13553
13554     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13555       border_element = Tile[xx][yy];    // may be moving!
13556     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13557       border_element = Tile[xx][yy];
13558     else if (MovDir[xx][yy] & touch_dir[i])     // elements are touching
13559       border_element = MovingOrBlocked2Element(xx, yy);
13560     else
13561       continue;                 // center and border element do not touch
13562
13563     border_element_old[i] = border_element;
13564   }
13565
13566   for (i = 0; i < NUM_DIRECTIONS; i++)
13567   {
13568     int xx = x + xy[i].x;
13569     int yy = y + xy[i].y;
13570     int center_side = trigger_sides[i][0];
13571     int border_element = border_element_old[i];
13572
13573     if (border_element == -1)
13574       continue;
13575
13576     // check for change of border element
13577     CheckElementChangeBySide(xx, yy, border_element, center_element,
13578                              CE_TOUCHING_X, center_side);
13579
13580     // (center element cannot be player, so we dont have to check this here)
13581   }
13582
13583   for (i = 0; i < NUM_DIRECTIONS; i++)
13584   {
13585     int xx = x + xy[i].x;
13586     int yy = y + xy[i].y;
13587     int border_side = trigger_sides[i][1];
13588     int border_element = border_element_old[i];
13589
13590     if (border_element == -1)
13591       continue;
13592
13593     // check for change of center element (but change it only once)
13594     if (!change_center_element)
13595       change_center_element =
13596         CheckElementChangeBySide(x, y, center_element, border_element,
13597                                  CE_TOUCHING_X, border_side);
13598
13599     if (IS_PLAYER(xx, yy))
13600     {
13601       /* use player element that is initially defined in the level playfield,
13602          not the player element that corresponds to the runtime player number
13603          (example: a level that contains EL_PLAYER_3 as the only player would
13604          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13605       int player_element = PLAYERINFO(xx, yy)->initial_element;
13606
13607       CheckElementChangeBySide(x, y, center_element, player_element,
13608                                CE_TOUCHING_X, border_side);
13609     }
13610   }
13611 }
13612
13613 void TestIfElementHitsCustomElement(int x, int y, int direction)
13614 {
13615   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13616   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13617   int hitx = x + dx, hity = y + dy;
13618   int hitting_element = Tile[x][y];
13619   int touched_element;
13620
13621   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13622     return;
13623
13624   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13625                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13626
13627   if (IN_LEV_FIELD(hitx, hity))
13628   {
13629     int opposite_direction = MV_DIR_OPPOSITE(direction);
13630     int hitting_side = direction;
13631     int touched_side = opposite_direction;
13632     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13633                           MovDir[hitx][hity] != direction ||
13634                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13635
13636     object_hit = TRUE;
13637
13638     if (object_hit)
13639     {
13640       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13641                                CE_HITTING_X, touched_side);
13642
13643       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13644                                CE_HIT_BY_X, hitting_side);
13645
13646       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13647                                CE_HIT_BY_SOMETHING, opposite_direction);
13648
13649       if (IS_PLAYER(hitx, hity))
13650       {
13651         /* use player element that is initially defined in the level playfield,
13652            not the player element that corresponds to the runtime player number
13653            (example: a level that contains EL_PLAYER_3 as the only player would
13654            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13655         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13656
13657         CheckElementChangeBySide(x, y, hitting_element, player_element,
13658                                  CE_HITTING_X, touched_side);
13659       }
13660     }
13661   }
13662
13663   // "hitting something" is also true when hitting the playfield border
13664   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13665                            CE_HITTING_SOMETHING, direction);
13666 }
13667
13668 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13669 {
13670   int i, kill_x = -1, kill_y = -1;
13671
13672   int bad_element = -1;
13673   struct XY *test_xy = xy_topdown;
13674   static int test_dir[4] =
13675   {
13676     MV_UP,
13677     MV_LEFT,
13678     MV_RIGHT,
13679     MV_DOWN
13680   };
13681
13682   for (i = 0; i < NUM_DIRECTIONS; i++)
13683   {
13684     int test_x, test_y, test_move_dir, test_element;
13685
13686     test_x = good_x + test_xy[i].x;
13687     test_y = good_y + test_xy[i].y;
13688
13689     if (!IN_LEV_FIELD(test_x, test_y))
13690       continue;
13691
13692     test_move_dir =
13693       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13694
13695     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13696
13697     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13698        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13699     */
13700     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13701         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13702     {
13703       kill_x = test_x;
13704       kill_y = test_y;
13705       bad_element = test_element;
13706
13707       break;
13708     }
13709   }
13710
13711   if (kill_x != -1 || kill_y != -1)
13712   {
13713     if (IS_PLAYER(good_x, good_y))
13714     {
13715       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13716
13717       if (player->shield_deadly_time_left > 0 &&
13718           !IS_INDESTRUCTIBLE(bad_element))
13719         Bang(kill_x, kill_y);
13720       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13721         KillPlayer(player);
13722     }
13723     else
13724       Bang(good_x, good_y);
13725   }
13726 }
13727
13728 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13729 {
13730   int i, kill_x = -1, kill_y = -1;
13731   int bad_element = Tile[bad_x][bad_y];
13732   struct XY *test_xy = xy_topdown;
13733   static int touch_dir[4] =
13734   {
13735     MV_LEFT | MV_RIGHT,
13736     MV_UP   | MV_DOWN,
13737     MV_UP   | MV_DOWN,
13738     MV_LEFT | MV_RIGHT
13739   };
13740   static int test_dir[4] =
13741   {
13742     MV_UP,
13743     MV_LEFT,
13744     MV_RIGHT,
13745     MV_DOWN
13746   };
13747
13748   if (bad_element == EL_EXPLOSION)      // skip just exploding bad things
13749     return;
13750
13751   for (i = 0; i < NUM_DIRECTIONS; i++)
13752   {
13753     int test_x, test_y, test_move_dir, test_element;
13754
13755     test_x = bad_x + test_xy[i].x;
13756     test_y = bad_y + test_xy[i].y;
13757
13758     if (!IN_LEV_FIELD(test_x, test_y))
13759       continue;
13760
13761     test_move_dir =
13762       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13763
13764     test_element = Tile[test_x][test_y];
13765
13766     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13767        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13768     */
13769     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13770         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13771     {
13772       // good thing is player or penguin that does not move away
13773       if (IS_PLAYER(test_x, test_y))
13774       {
13775         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13776
13777         if (bad_element == EL_ROBOT && player->is_moving)
13778           continue;     // robot does not kill player if he is moving
13779
13780         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13781         {
13782           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13783             continue;           // center and border element do not touch
13784         }
13785
13786         kill_x = test_x;
13787         kill_y = test_y;
13788
13789         break;
13790       }
13791       else if (test_element == EL_PENGUIN)
13792       {
13793         kill_x = test_x;
13794         kill_y = test_y;
13795
13796         break;
13797       }
13798     }
13799   }
13800
13801   if (kill_x != -1 || kill_y != -1)
13802   {
13803     if (IS_PLAYER(kill_x, kill_y))
13804     {
13805       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13806
13807       if (player->shield_deadly_time_left > 0 &&
13808           !IS_INDESTRUCTIBLE(bad_element))
13809         Bang(bad_x, bad_y);
13810       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13811         KillPlayer(player);
13812     }
13813     else
13814       Bang(kill_x, kill_y);
13815   }
13816 }
13817
13818 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13819 {
13820   int bad_element = Tile[bad_x][bad_y];
13821   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13822   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13823   int test_x = bad_x + dx, test_y = bad_y + dy;
13824   int test_move_dir, test_element;
13825   int kill_x = -1, kill_y = -1;
13826
13827   if (!IN_LEV_FIELD(test_x, test_y))
13828     return;
13829
13830   test_move_dir =
13831     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13832
13833   test_element = Tile[test_x][test_y];
13834
13835   if (test_move_dir != bad_move_dir)
13836   {
13837     // good thing can be player or penguin that does not move away
13838     if (IS_PLAYER(test_x, test_y))
13839     {
13840       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13841
13842       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13843          player as being hit when he is moving towards the bad thing, because
13844          the "get hit by" condition would be lost after the player stops) */
13845       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13846         return;         // player moves away from bad thing
13847
13848       kill_x = test_x;
13849       kill_y = test_y;
13850     }
13851     else if (test_element == EL_PENGUIN)
13852     {
13853       kill_x = test_x;
13854       kill_y = test_y;
13855     }
13856   }
13857
13858   if (kill_x != -1 || kill_y != -1)
13859   {
13860     if (IS_PLAYER(kill_x, kill_y))
13861     {
13862       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13863
13864       if (player->shield_deadly_time_left > 0 &&
13865           !IS_INDESTRUCTIBLE(bad_element))
13866         Bang(bad_x, bad_y);
13867       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13868         KillPlayer(player);
13869     }
13870     else
13871       Bang(kill_x, kill_y);
13872   }
13873 }
13874
13875 void TestIfPlayerTouchesBadThing(int x, int y)
13876 {
13877   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13878 }
13879
13880 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13881 {
13882   TestIfGoodThingHitsBadThing(x, y, move_dir);
13883 }
13884
13885 void TestIfBadThingTouchesPlayer(int x, int y)
13886 {
13887   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13888 }
13889
13890 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13891 {
13892   TestIfBadThingHitsGoodThing(x, y, move_dir);
13893 }
13894
13895 void TestIfFriendTouchesBadThing(int x, int y)
13896 {
13897   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13898 }
13899
13900 void TestIfBadThingTouchesFriend(int x, int y)
13901 {
13902   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13903 }
13904
13905 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13906 {
13907   int i, kill_x = bad_x, kill_y = bad_y;
13908   struct XY *xy = xy_topdown;
13909
13910   for (i = 0; i < NUM_DIRECTIONS; i++)
13911   {
13912     int x, y, element;
13913
13914     x = bad_x + xy[i].x;
13915     y = bad_y + xy[i].y;
13916     if (!IN_LEV_FIELD(x, y))
13917       continue;
13918
13919     element = Tile[x][y];
13920     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13921         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13922     {
13923       kill_x = x;
13924       kill_y = y;
13925       break;
13926     }
13927   }
13928
13929   if (kill_x != bad_x || kill_y != bad_y)
13930     Bang(bad_x, bad_y);
13931 }
13932
13933 void KillPlayer(struct PlayerInfo *player)
13934 {
13935   int jx = player->jx, jy = player->jy;
13936
13937   if (!player->active)
13938     return;
13939
13940 #if 0
13941   Debug("game:playing:KillPlayer",
13942         "0: killed == %d, active == %d, reanimated == %d",
13943         player->killed, player->active, player->reanimated);
13944 #endif
13945
13946   /* the following code was introduced to prevent an infinite loop when calling
13947      -> Bang()
13948      -> CheckTriggeredElementChangeExt()
13949      -> ExecuteCustomElementAction()
13950      -> KillPlayer()
13951      -> (infinitely repeating the above sequence of function calls)
13952      which occurs when killing the player while having a CE with the setting
13953      "kill player X when explosion of <player X>"; the solution using a new
13954      field "player->killed" was chosen for backwards compatibility, although
13955      clever use of the fields "player->active" etc. would probably also work */
13956 #if 1
13957   if (player->killed)
13958     return;
13959 #endif
13960
13961   player->killed = TRUE;
13962
13963   // remove accessible field at the player's position
13964   RemoveField(jx, jy);
13965
13966   // deactivate shield (else Bang()/Explode() would not work right)
13967   player->shield_normal_time_left = 0;
13968   player->shield_deadly_time_left = 0;
13969
13970 #if 0
13971   Debug("game:playing:KillPlayer",
13972         "1: killed == %d, active == %d, reanimated == %d",
13973         player->killed, player->active, player->reanimated);
13974 #endif
13975
13976   Bang(jx, jy);
13977
13978 #if 0
13979   Debug("game:playing:KillPlayer",
13980         "2: killed == %d, active == %d, reanimated == %d",
13981         player->killed, player->active, player->reanimated);
13982 #endif
13983
13984   if (player->reanimated)       // killed player may have been reanimated
13985     player->killed = player->reanimated = FALSE;
13986   else
13987     BuryPlayer(player);
13988 }
13989
13990 static void KillPlayerUnlessEnemyProtected(int x, int y)
13991 {
13992   if (!PLAYER_ENEMY_PROTECTED(x, y))
13993     KillPlayer(PLAYERINFO(x, y));
13994 }
13995
13996 static void KillPlayerUnlessExplosionProtected(int x, int y)
13997 {
13998   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13999     KillPlayer(PLAYERINFO(x, y));
14000 }
14001
14002 void BuryPlayer(struct PlayerInfo *player)
14003 {
14004   int jx = player->jx, jy = player->jy;
14005
14006   if (!player->active)
14007     return;
14008
14009   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14010
14011   RemovePlayer(player);
14012
14013   player->buried = TRUE;
14014
14015   if (game.all_players_gone)
14016     game.GameOver = TRUE;
14017 }
14018
14019 void RemovePlayer(struct PlayerInfo *player)
14020 {
14021   int jx = player->jx, jy = player->jy;
14022   int i, found = FALSE;
14023
14024   player->present = FALSE;
14025   player->active = FALSE;
14026
14027   // required for some CE actions (even if the player is not active anymore)
14028   player->MovPos = 0;
14029
14030   if (!ExplodeField[jx][jy])
14031     StorePlayer[jx][jy] = 0;
14032
14033   if (player->is_moving)
14034     TEST_DrawLevelField(player->last_jx, player->last_jy);
14035
14036   for (i = 0; i < MAX_PLAYERS; i++)
14037     if (stored_player[i].active)
14038       found = TRUE;
14039
14040   if (!found)
14041   {
14042     game.all_players_gone = TRUE;
14043     game.GameOver = TRUE;
14044   }
14045
14046   game.exit_x = game.robot_wheel_x = jx;
14047   game.exit_y = game.robot_wheel_y = jy;
14048 }
14049
14050 void ExitPlayer(struct PlayerInfo *player)
14051 {
14052   DrawPlayer(player);   // needed here only to cleanup last field
14053   RemovePlayer(player);
14054
14055   if (game.players_still_needed > 0)
14056     game.players_still_needed--;
14057 }
14058
14059 static void SetFieldForSnapping(int x, int y, int element, int direction,
14060                                 int player_index_bit)
14061 {
14062   struct ElementInfo *ei = &element_info[element];
14063   int direction_bit = MV_DIR_TO_BIT(direction);
14064   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14065   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14066                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14067
14068   Tile[x][y] = EL_ELEMENT_SNAPPING;
14069   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14070   MovDir[x][y] = direction;
14071   Store[x][y] = element;
14072   Store2[x][y] = player_index_bit;
14073
14074   ResetGfxAnimation(x, y);
14075
14076   GfxElement[x][y] = element;
14077   GfxAction[x][y] = action;
14078   GfxDir[x][y] = direction;
14079   GfxFrame[x][y] = -1;
14080 }
14081
14082 static void TestFieldAfterSnapping(int x, int y, int element, int direction,
14083                                    int player_index_bit)
14084 {
14085   TestIfElementTouchesCustomElement(x, y);      // for empty space
14086
14087   if (level.finish_dig_collect)
14088   {
14089     int dig_side = MV_DIR_OPPOSITE(direction);
14090     int change_event = (IS_DIGGABLE(element) ? CE_PLAYER_DIGS_X :
14091                         CE_PLAYER_COLLECTS_X);
14092
14093     CheckTriggeredElementChangeByPlayer(x, y, element, change_event,
14094                                         player_index_bit, dig_side);
14095     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14096                                         player_index_bit, dig_side);
14097   }
14098 }
14099
14100 /*
14101   =============================================================================
14102   checkDiagonalPushing()
14103   -----------------------------------------------------------------------------
14104   check if diagonal input device direction results in pushing of object
14105   (by checking if the alternative direction is walkable, diggable, ...)
14106   =============================================================================
14107 */
14108
14109 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14110                                     int x, int y, int real_dx, int real_dy)
14111 {
14112   int jx, jy, dx, dy, xx, yy;
14113
14114   if (real_dx == 0 || real_dy == 0)     // no diagonal direction => push
14115     return TRUE;
14116
14117   // diagonal direction: check alternative direction
14118   jx = player->jx;
14119   jy = player->jy;
14120   dx = x - jx;
14121   dy = y - jy;
14122   xx = jx + (dx == 0 ? real_dx : 0);
14123   yy = jy + (dy == 0 ? real_dy : 0);
14124
14125   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Tile[xx][yy]));
14126 }
14127
14128 /*
14129   =============================================================================
14130   DigField()
14131   -----------------------------------------------------------------------------
14132   x, y:                 field next to player (non-diagonal) to try to dig to
14133   real_dx, real_dy:     direction as read from input device (can be diagonal)
14134   =============================================================================
14135 */
14136
14137 static int DigField(struct PlayerInfo *player,
14138                     int oldx, int oldy, int x, int y,
14139                     int real_dx, int real_dy, int mode)
14140 {
14141   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14142   boolean player_was_pushing = player->is_pushing;
14143   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14144   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14145   int jx = oldx, jy = oldy;
14146   int dx = x - jx, dy = y - jy;
14147   int nextx = x + dx, nexty = y + dy;
14148   int move_direction = (dx == -1 ? MV_LEFT  :
14149                         dx == +1 ? MV_RIGHT :
14150                         dy == -1 ? MV_UP    :
14151                         dy == +1 ? MV_DOWN  : MV_NONE);
14152   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14153   int dig_side = MV_DIR_OPPOSITE(move_direction);
14154   int old_element = Tile[jx][jy];
14155   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14156   int collect_count;
14157
14158   if (is_player)                // function can also be called by EL_PENGUIN
14159   {
14160     if (player->MovPos == 0)
14161     {
14162       player->is_digging = FALSE;
14163       player->is_collecting = FALSE;
14164     }
14165
14166     if (player->MovPos == 0)    // last pushing move finished
14167       player->is_pushing = FALSE;
14168
14169     if (mode == DF_NO_PUSH)     // player just stopped pushing
14170     {
14171       player->is_switching = FALSE;
14172       player->push_delay = -1;
14173
14174       return MP_NO_ACTION;
14175     }
14176   }
14177   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14178     old_element = Back[jx][jy];
14179
14180   // in case of element dropped at player position, check background
14181   else if (Back[jx][jy] != EL_EMPTY &&
14182            game.engine_version >= VERSION_IDENT(2,2,0,0))
14183     old_element = Back[jx][jy];
14184
14185   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14186     return MP_NO_ACTION;        // field has no opening in this direction
14187
14188   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element, opposite_direction))
14189     return MP_NO_ACTION;        // field has no opening in this direction
14190
14191   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14192   {
14193     SplashAcid(x, y);
14194
14195     Tile[jx][jy] = player->artwork_element;
14196     InitMovingField(jx, jy, MV_DOWN);
14197     Store[jx][jy] = EL_ACID;
14198     ContinueMoving(jx, jy);
14199     BuryPlayer(player);
14200
14201     return MP_DONT_RUN_INTO;
14202   }
14203
14204   if (player_can_move && DONT_RUN_INTO(element))
14205   {
14206     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14207
14208     return MP_DONT_RUN_INTO;
14209   }
14210
14211   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14212     return MP_NO_ACTION;
14213
14214   collect_count = element_info[element].collect_count_initial;
14215
14216   if (!is_player && !IS_COLLECTIBLE(element))   // penguin cannot collect it
14217     return MP_NO_ACTION;
14218
14219   if (game.engine_version < VERSION_IDENT(2,2,0,0))
14220     player_can_move = player_can_move_or_snap;
14221
14222   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14223       game.engine_version >= VERSION_IDENT(2,2,0,0))
14224   {
14225     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14226                                player->index_bit, dig_side);
14227     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14228                                         player->index_bit, dig_side);
14229
14230     if (element == EL_DC_LANDMINE)
14231       Bang(x, y);
14232
14233     if (Tile[x][y] != element)          // field changed by snapping
14234       return MP_ACTION;
14235
14236     return MP_NO_ACTION;
14237   }
14238
14239   if (player->gravity && is_player && !player->is_auto_moving &&
14240       canFallDown(player) && move_direction != MV_DOWN &&
14241       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14242     return MP_NO_ACTION;        // player cannot walk here due to gravity
14243
14244   if (player_can_move &&
14245       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14246   {
14247     int sound_element = SND_ELEMENT(element);
14248     int sound_action = ACTION_WALKING;
14249
14250     if (IS_RND_GATE(element))
14251     {
14252       if (!player->key[RND_GATE_NR(element)])
14253         return MP_NO_ACTION;
14254     }
14255     else if (IS_RND_GATE_GRAY(element))
14256     {
14257       if (!player->key[RND_GATE_GRAY_NR(element)])
14258         return MP_NO_ACTION;
14259     }
14260     else if (IS_RND_GATE_GRAY_ACTIVE(element))
14261     {
14262       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14263         return MP_NO_ACTION;
14264     }
14265     else if (element == EL_EXIT_OPEN ||
14266              element == EL_EM_EXIT_OPEN ||
14267              element == EL_EM_EXIT_OPENING ||
14268              element == EL_STEEL_EXIT_OPEN ||
14269              element == EL_EM_STEEL_EXIT_OPEN ||
14270              element == EL_EM_STEEL_EXIT_OPENING ||
14271              element == EL_SP_EXIT_OPEN ||
14272              element == EL_SP_EXIT_OPENING)
14273     {
14274       sound_action = ACTION_PASSING;    // player is passing exit
14275     }
14276     else if (element == EL_EMPTY)
14277     {
14278       sound_action = ACTION_MOVING;             // nothing to walk on
14279     }
14280
14281     // play sound from background or player, whatever is available
14282     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14283       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14284     else
14285       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14286   }
14287   else if (player_can_move &&
14288            IS_PASSABLE(element) && canPassField(x, y, move_direction))
14289   {
14290     if (!ACCESS_FROM(element, opposite_direction))
14291       return MP_NO_ACTION;      // field not accessible from this direction
14292
14293     if (CAN_MOVE(element))      // only fixed elements can be passed!
14294       return MP_NO_ACTION;
14295
14296     if (IS_EM_GATE(element))
14297     {
14298       if (!player->key[EM_GATE_NR(element)])
14299         return MP_NO_ACTION;
14300     }
14301     else if (IS_EM_GATE_GRAY(element))
14302     {
14303       if (!player->key[EM_GATE_GRAY_NR(element)])
14304         return MP_NO_ACTION;
14305     }
14306     else if (IS_EM_GATE_GRAY_ACTIVE(element))
14307     {
14308       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14309         return MP_NO_ACTION;
14310     }
14311     else if (IS_EMC_GATE(element))
14312     {
14313       if (!player->key[EMC_GATE_NR(element)])
14314         return MP_NO_ACTION;
14315     }
14316     else if (IS_EMC_GATE_GRAY(element))
14317     {
14318       if (!player->key[EMC_GATE_GRAY_NR(element)])
14319         return MP_NO_ACTION;
14320     }
14321     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14322     {
14323       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14324         return MP_NO_ACTION;
14325     }
14326     else if (element == EL_DC_GATE_WHITE ||
14327              element == EL_DC_GATE_WHITE_GRAY ||
14328              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14329     {
14330       if (player->num_white_keys == 0)
14331         return MP_NO_ACTION;
14332
14333       player->num_white_keys--;
14334     }
14335     else if (IS_SP_PORT(element))
14336     {
14337       if (element == EL_SP_GRAVITY_PORT_LEFT ||
14338           element == EL_SP_GRAVITY_PORT_RIGHT ||
14339           element == EL_SP_GRAVITY_PORT_UP ||
14340           element == EL_SP_GRAVITY_PORT_DOWN)
14341         player->gravity = !player->gravity;
14342       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14343                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14344                element == EL_SP_GRAVITY_ON_PORT_UP ||
14345                element == EL_SP_GRAVITY_ON_PORT_DOWN)
14346         player->gravity = TRUE;
14347       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14348                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14349                element == EL_SP_GRAVITY_OFF_PORT_UP ||
14350                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14351         player->gravity = FALSE;
14352     }
14353
14354     // automatically move to the next field with double speed
14355     player->programmed_action = move_direction;
14356
14357     if (player->move_delay_reset_counter == 0)
14358     {
14359       player->move_delay_reset_counter = 2;     // two double speed steps
14360
14361       DOUBLE_PLAYER_SPEED(player);
14362     }
14363
14364     PlayLevelSoundAction(x, y, ACTION_PASSING);
14365   }
14366   else if (player_can_move_or_snap && IS_DIGGABLE(element))
14367   {
14368     RemoveField(x, y);
14369
14370     if (mode != DF_SNAP)
14371     {
14372       GfxElement[x][y] = GFX_ELEMENT(element);
14373       player->is_digging = TRUE;
14374     }
14375
14376     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14377
14378     // use old behaviour for old levels (digging)
14379     if (!level.finish_dig_collect)
14380     {
14381       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14382                                           player->index_bit, dig_side);
14383
14384       // if digging triggered player relocation, finish digging tile
14385       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14386         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14387     }
14388
14389     if (mode == DF_SNAP)
14390     {
14391       if (level.block_snap_field)
14392         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14393       else
14394         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14395
14396       // use old behaviour for old levels (snapping)
14397       if (!level.finish_dig_collect)
14398         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14399                                             player->index_bit, dig_side);
14400     }
14401   }
14402   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14403   {
14404     RemoveField(x, y);
14405
14406     if (is_player && mode != DF_SNAP)
14407     {
14408       GfxElement[x][y] = element;
14409       player->is_collecting = TRUE;
14410     }
14411
14412     if (element == EL_SPEED_PILL)
14413     {
14414       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14415     }
14416     else if (element == EL_EXTRA_TIME && level.time > 0)
14417     {
14418       TimeLeft += level.extra_time;
14419
14420       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14421
14422       DisplayGameControlValues();
14423     }
14424     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14425     {
14426       int shield_time = (element == EL_SHIELD_DEADLY ?
14427                          level.shield_deadly_time :
14428                          level.shield_normal_time);
14429
14430       player->shield_normal_time_left += shield_time;
14431       if (element == EL_SHIELD_DEADLY)
14432         player->shield_deadly_time_left += shield_time;
14433     }
14434     else if (element == EL_DYNAMITE ||
14435              element == EL_EM_DYNAMITE ||
14436              element == EL_SP_DISK_RED)
14437     {
14438       if (player->inventory_size < MAX_INVENTORY_SIZE)
14439         player->inventory_element[player->inventory_size++] = element;
14440
14441       DrawGameDoorValues();
14442     }
14443     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14444     {
14445       player->dynabomb_count++;
14446       player->dynabombs_left++;
14447     }
14448     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14449     {
14450       player->dynabomb_size++;
14451     }
14452     else if (element == EL_DYNABOMB_INCREASE_POWER)
14453     {
14454       player->dynabomb_xl = TRUE;
14455     }
14456     else if (IS_KEY(element))
14457     {
14458       player->key[KEY_NR(element)] = TRUE;
14459
14460       DrawGameDoorValues();
14461     }
14462     else if (element == EL_DC_KEY_WHITE)
14463     {
14464       player->num_white_keys++;
14465
14466       // display white keys?
14467       // DrawGameDoorValues();
14468     }
14469     else if (IS_ENVELOPE(element))
14470     {
14471       boolean wait_for_snapping = (mode == DF_SNAP && level.block_snap_field);
14472
14473       if (!wait_for_snapping)
14474         player->show_envelope = element;
14475     }
14476     else if (element == EL_EMC_LENSES)
14477     {
14478       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14479
14480       RedrawAllInvisibleElementsForLenses();
14481     }
14482     else if (element == EL_EMC_MAGNIFIER)
14483     {
14484       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14485
14486       RedrawAllInvisibleElementsForMagnifier();
14487     }
14488     else if (IS_DROPPABLE(element) ||
14489              IS_THROWABLE(element))     // can be collected and dropped
14490     {
14491       int i;
14492
14493       if (collect_count == 0)
14494         player->inventory_infinite_element = element;
14495       else
14496         for (i = 0; i < collect_count; i++)
14497           if (player->inventory_size < MAX_INVENTORY_SIZE)
14498             player->inventory_element[player->inventory_size++] = element;
14499
14500       DrawGameDoorValues();
14501     }
14502     else if (collect_count > 0)
14503     {
14504       game.gems_still_needed -= collect_count;
14505       if (game.gems_still_needed < 0)
14506         game.gems_still_needed = 0;
14507
14508       game.snapshot.collected_item = TRUE;
14509
14510       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
14511
14512       DisplayGameControlValues();
14513     }
14514
14515     RaiseScoreElement(element);
14516     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14517
14518     // use old behaviour for old levels (collecting)
14519     if (!level.finish_dig_collect && is_player)
14520     {
14521       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14522                                           player->index_bit, dig_side);
14523
14524       // if collecting triggered player relocation, finish collecting tile
14525       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14526         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14527     }
14528
14529     if (mode == DF_SNAP)
14530     {
14531       if (level.block_snap_field)
14532         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14533       else
14534         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14535
14536       // use old behaviour for old levels (snapping)
14537       if (!level.finish_dig_collect)
14538         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14539                                             player->index_bit, dig_side);
14540     }
14541   }
14542   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14543   {
14544     if (mode == DF_SNAP && element != EL_BD_ROCK)
14545       return MP_NO_ACTION;
14546
14547     if (CAN_FALL(element) && dy)
14548       return MP_NO_ACTION;
14549
14550     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14551         !(element == EL_SPRING && level.use_spring_bug))
14552       return MP_NO_ACTION;
14553
14554     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14555         ((move_direction & MV_VERTICAL &&
14556           ((element_info[element].move_pattern & MV_LEFT &&
14557             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14558            (element_info[element].move_pattern & MV_RIGHT &&
14559             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14560          (move_direction & MV_HORIZONTAL &&
14561           ((element_info[element].move_pattern & MV_UP &&
14562             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14563            (element_info[element].move_pattern & MV_DOWN &&
14564             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14565       return MP_NO_ACTION;
14566
14567     // do not push elements already moving away faster than player
14568     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14569         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14570       return MP_NO_ACTION;
14571
14572     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14573     {
14574       if (player->push_delay_value == -1 || !player_was_pushing)
14575         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14576     }
14577     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14578     {
14579       if (player->push_delay_value == -1)
14580         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14581     }
14582     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14583     {
14584       if (!player->is_pushing)
14585         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14586     }
14587
14588     player->is_pushing = TRUE;
14589     player->is_active = TRUE;
14590
14591     if (!(IN_LEV_FIELD(nextx, nexty) &&
14592           (IS_FREE(nextx, nexty) ||
14593            (IS_SB_ELEMENT(element) &&
14594             Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14595            (IS_CUSTOM_ELEMENT(element) &&
14596             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
14597       return MP_NO_ACTION;
14598
14599     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14600       return MP_NO_ACTION;
14601
14602     if (player->push_delay == -1)       // new pushing; restart delay
14603       player->push_delay = 0;
14604
14605     if (player->push_delay < player->push_delay_value &&
14606         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14607         element != EL_SPRING && element != EL_BALLOON)
14608     {
14609       // make sure that there is no move delay before next try to push
14610       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14611         player->move_delay = 0;
14612
14613       return MP_NO_ACTION;
14614     }
14615
14616     if (IS_CUSTOM_ELEMENT(element) &&
14617         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14618     {
14619       if (!DigFieldByCE(nextx, nexty, element))
14620         return MP_NO_ACTION;
14621     }
14622
14623     if (IS_SB_ELEMENT(element))
14624     {
14625       boolean sokoban_task_solved = FALSE;
14626
14627       if (element == EL_SOKOBAN_FIELD_FULL)
14628       {
14629         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14630
14631         IncrementSokobanFieldsNeeded();
14632         IncrementSokobanObjectsNeeded();
14633       }
14634
14635       if (Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14636       {
14637         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14638
14639         DecrementSokobanFieldsNeeded();
14640         DecrementSokobanObjectsNeeded();
14641
14642         // sokoban object was pushed from empty field to sokoban field
14643         if (Back[x][y] == EL_EMPTY)
14644           sokoban_task_solved = TRUE;
14645       }
14646
14647       Tile[x][y] = EL_SOKOBAN_OBJECT;
14648
14649       if (Back[x][y] == Back[nextx][nexty])
14650         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14651       else if (Back[x][y] != 0)
14652         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14653                                     ACTION_EMPTYING);
14654       else
14655         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14656                                     ACTION_FILLING);
14657
14658       if (sokoban_task_solved &&
14659           game.sokoban_fields_still_needed == 0 &&
14660           game.sokoban_objects_still_needed == 0 &&
14661           level.auto_exit_sokoban)
14662       {
14663         game.players_still_needed = 0;
14664
14665         LevelSolved();
14666
14667         PlaySound(SND_GAME_SOKOBAN_SOLVING);
14668       }
14669     }
14670     else
14671       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14672
14673     InitMovingField(x, y, move_direction);
14674     GfxAction[x][y] = ACTION_PUSHING;
14675
14676     if (mode == DF_SNAP)
14677       ContinueMoving(x, y);
14678     else
14679       MovPos[x][y] = (dx != 0 ? dx : dy);
14680
14681     Pushed[x][y] = TRUE;
14682     Pushed[nextx][nexty] = TRUE;
14683
14684     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14685       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14686     else
14687       player->push_delay_value = -1;    // get new value later
14688
14689     // check for element change _after_ element has been pushed
14690     if (game.use_change_when_pushing_bug)
14691     {
14692       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14693                                  player->index_bit, dig_side);
14694       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14695                                           player->index_bit, dig_side);
14696     }
14697   }
14698   else if (IS_SWITCHABLE(element))
14699   {
14700     if (PLAYER_SWITCHING(player, x, y))
14701     {
14702       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14703                                           player->index_bit, dig_side);
14704
14705       return MP_ACTION;
14706     }
14707
14708     player->is_switching = TRUE;
14709     player->switch_x = x;
14710     player->switch_y = y;
14711
14712     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14713
14714     if (element == EL_ROBOT_WHEEL)
14715     {
14716       Tile[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14717
14718       game.robot_wheel_x = x;
14719       game.robot_wheel_y = y;
14720       game.robot_wheel_active = TRUE;
14721
14722       TEST_DrawLevelField(x, y);
14723     }
14724     else if (element == EL_SP_TERMINAL)
14725     {
14726       int xx, yy;
14727
14728       SCAN_PLAYFIELD(xx, yy)
14729       {
14730         if (Tile[xx][yy] == EL_SP_DISK_YELLOW)
14731         {
14732           Bang(xx, yy);
14733         }
14734         else if (Tile[xx][yy] == EL_SP_TERMINAL)
14735         {
14736           Tile[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14737
14738           ResetGfxAnimation(xx, yy);
14739           TEST_DrawLevelField(xx, yy);
14740         }
14741       }
14742     }
14743     else if (IS_BELT_SWITCH(element))
14744     {
14745       ToggleBeltSwitch(x, y);
14746     }
14747     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14748              element == EL_SWITCHGATE_SWITCH_DOWN ||
14749              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14750              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14751     {
14752       ToggleSwitchgateSwitch();
14753     }
14754     else if (element == EL_LIGHT_SWITCH ||
14755              element == EL_LIGHT_SWITCH_ACTIVE)
14756     {
14757       ToggleLightSwitch(x, y);
14758     }
14759     else if (element == EL_TIMEGATE_SWITCH ||
14760              element == EL_DC_TIMEGATE_SWITCH)
14761     {
14762       ActivateTimegateSwitch(x, y);
14763     }
14764     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14765              element == EL_BALLOON_SWITCH_RIGHT ||
14766              element == EL_BALLOON_SWITCH_UP    ||
14767              element == EL_BALLOON_SWITCH_DOWN  ||
14768              element == EL_BALLOON_SWITCH_NONE  ||
14769              element == EL_BALLOON_SWITCH_ANY)
14770     {
14771       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14772                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14773                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14774                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14775                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14776                              move_direction);
14777     }
14778     else if (element == EL_LAMP)
14779     {
14780       Tile[x][y] = EL_LAMP_ACTIVE;
14781       game.lights_still_needed--;
14782
14783       ResetGfxAnimation(x, y);
14784       TEST_DrawLevelField(x, y);
14785     }
14786     else if (element == EL_TIME_ORB_FULL)
14787     {
14788       Tile[x][y] = EL_TIME_ORB_EMPTY;
14789
14790       if (level.time > 0 || level.use_time_orb_bug)
14791       {
14792         TimeLeft += level.time_orb_time;
14793         game.no_level_time_limit = FALSE;
14794
14795         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14796
14797         DisplayGameControlValues();
14798       }
14799
14800       ResetGfxAnimation(x, y);
14801       TEST_DrawLevelField(x, y);
14802     }
14803     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14804              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14805     {
14806       int xx, yy;
14807
14808       game.ball_active = !game.ball_active;
14809
14810       SCAN_PLAYFIELD(xx, yy)
14811       {
14812         int e = Tile[xx][yy];
14813
14814         if (game.ball_active)
14815         {
14816           if (e == EL_EMC_MAGIC_BALL)
14817             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14818           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14819             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14820         }
14821         else
14822         {
14823           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14824             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14825           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14826             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14827         }
14828       }
14829     }
14830
14831     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14832                                         player->index_bit, dig_side);
14833
14834     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14835                                         player->index_bit, dig_side);
14836
14837     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14838                                         player->index_bit, dig_side);
14839
14840     return MP_ACTION;
14841   }
14842   else
14843   {
14844     if (!PLAYER_SWITCHING(player, x, y))
14845     {
14846       player->is_switching = TRUE;
14847       player->switch_x = x;
14848       player->switch_y = y;
14849
14850       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14851                                  player->index_bit, dig_side);
14852       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14853                                           player->index_bit, dig_side);
14854
14855       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14856                                  player->index_bit, dig_side);
14857       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14858                                           player->index_bit, dig_side);
14859     }
14860
14861     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14862                                player->index_bit, dig_side);
14863     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14864                                         player->index_bit, dig_side);
14865
14866     return MP_NO_ACTION;
14867   }
14868
14869   player->push_delay = -1;
14870
14871   if (is_player)                // function can also be called by EL_PENGUIN
14872   {
14873     if (Tile[x][y] != element)          // really digged/collected something
14874     {
14875       player->is_collecting = !player->is_digging;
14876       player->is_active = TRUE;
14877
14878       player->last_removed_element = element;
14879     }
14880   }
14881
14882   return MP_MOVING;
14883 }
14884
14885 static boolean DigFieldByCE(int x, int y, int digging_element)
14886 {
14887   int element = Tile[x][y];
14888
14889   if (!IS_FREE(x, y))
14890   {
14891     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14892                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14893                   ACTION_BREAKING);
14894
14895     // no element can dig solid indestructible elements
14896     if (IS_INDESTRUCTIBLE(element) &&
14897         !IS_DIGGABLE(element) &&
14898         !IS_COLLECTIBLE(element))
14899       return FALSE;
14900
14901     if (AmoebaNr[x][y] &&
14902         (element == EL_AMOEBA_FULL ||
14903          element == EL_BD_AMOEBA ||
14904          element == EL_AMOEBA_GROWING))
14905     {
14906       AmoebaCnt[AmoebaNr[x][y]]--;
14907       AmoebaCnt2[AmoebaNr[x][y]]--;
14908     }
14909
14910     if (IS_MOVING(x, y))
14911       RemoveMovingField(x, y);
14912     else
14913     {
14914       RemoveField(x, y);
14915       TEST_DrawLevelField(x, y);
14916     }
14917
14918     // if digged element was about to explode, prevent the explosion
14919     ExplodeField[x][y] = EX_TYPE_NONE;
14920
14921     PlayLevelSoundAction(x, y, action);
14922   }
14923
14924   Store[x][y] = EL_EMPTY;
14925
14926   // this makes it possible to leave the removed element again
14927   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14928     Store[x][y] = element;
14929
14930   return TRUE;
14931 }
14932
14933 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14934 {
14935   int jx = player->jx, jy = player->jy;
14936   int x = jx + dx, y = jy + dy;
14937   int snap_direction = (dx == -1 ? MV_LEFT  :
14938                         dx == +1 ? MV_RIGHT :
14939                         dy == -1 ? MV_UP    :
14940                         dy == +1 ? MV_DOWN  : MV_NONE);
14941   boolean can_continue_snapping = (level.continuous_snapping &&
14942                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14943
14944   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14945     return FALSE;
14946
14947   if (!player->active || !IN_LEV_FIELD(x, y))
14948     return FALSE;
14949
14950   if (dx && dy)
14951     return FALSE;
14952
14953   if (!dx && !dy)
14954   {
14955     if (player->MovPos == 0)
14956       player->is_pushing = FALSE;
14957
14958     player->is_snapping = FALSE;
14959
14960     if (player->MovPos == 0)
14961     {
14962       player->is_moving = FALSE;
14963       player->is_digging = FALSE;
14964       player->is_collecting = FALSE;
14965     }
14966
14967     return FALSE;
14968   }
14969
14970   // prevent snapping with already pressed snap key when not allowed
14971   if (player->is_snapping && !can_continue_snapping)
14972     return FALSE;
14973
14974   player->MovDir = snap_direction;
14975
14976   if (player->MovPos == 0)
14977   {
14978     player->is_moving = FALSE;
14979     player->is_digging = FALSE;
14980     player->is_collecting = FALSE;
14981   }
14982
14983   player->is_dropping = FALSE;
14984   player->is_dropping_pressed = FALSE;
14985   player->drop_pressed_delay = 0;
14986
14987   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14988     return FALSE;
14989
14990   player->is_snapping = TRUE;
14991   player->is_active = TRUE;
14992
14993   if (player->MovPos == 0)
14994   {
14995     player->is_moving = FALSE;
14996     player->is_digging = FALSE;
14997     player->is_collecting = FALSE;
14998   }
14999
15000   if (player->MovPos != 0)      // prevent graphic bugs in versions < 2.2.0
15001     TEST_DrawLevelField(player->last_jx, player->last_jy);
15002
15003   TEST_DrawLevelField(x, y);
15004
15005   return TRUE;
15006 }
15007
15008 static boolean DropElement(struct PlayerInfo *player)
15009 {
15010   int old_element, new_element;
15011   int dropx = player->jx, dropy = player->jy;
15012   int drop_direction = player->MovDir;
15013   int drop_side = drop_direction;
15014   int drop_element = get_next_dropped_element(player);
15015
15016   /* do not drop an element on top of another element; when holding drop key
15017      pressed without moving, dropped element must move away before the next
15018      element can be dropped (this is especially important if the next element
15019      is dynamite, which can be placed on background for historical reasons) */
15020   if (PLAYER_DROPPING(player, dropx, dropy) && Tile[dropx][dropy] != EL_EMPTY)
15021     return MP_ACTION;
15022
15023   if (IS_THROWABLE(drop_element))
15024   {
15025     dropx += GET_DX_FROM_DIR(drop_direction);
15026     dropy += GET_DY_FROM_DIR(drop_direction);
15027
15028     if (!IN_LEV_FIELD(dropx, dropy))
15029       return FALSE;
15030   }
15031
15032   old_element = Tile[dropx][dropy];     // old element at dropping position
15033   new_element = drop_element;           // default: no change when dropping
15034
15035   // check if player is active, not moving and ready to drop
15036   if (!player->active || player->MovPos || player->drop_delay > 0)
15037     return FALSE;
15038
15039   // check if player has anything that can be dropped
15040   if (new_element == EL_UNDEFINED)
15041     return FALSE;
15042
15043   // only set if player has anything that can be dropped
15044   player->is_dropping_pressed = TRUE;
15045
15046   // check if drop key was pressed long enough for EM style dynamite
15047   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15048     return FALSE;
15049
15050   // check if anything can be dropped at the current position
15051   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15052     return FALSE;
15053
15054   // collected custom elements can only be dropped on empty fields
15055   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15056     return FALSE;
15057
15058   if (old_element != EL_EMPTY)
15059     Back[dropx][dropy] = old_element;   // store old element on this field
15060
15061   ResetGfxAnimation(dropx, dropy);
15062   ResetRandomAnimationValue(dropx, dropy);
15063
15064   if (player->inventory_size > 0 ||
15065       player->inventory_infinite_element != EL_UNDEFINED)
15066   {
15067     if (player->inventory_size > 0)
15068     {
15069       player->inventory_size--;
15070
15071       DrawGameDoorValues();
15072
15073       if (new_element == EL_DYNAMITE)
15074         new_element = EL_DYNAMITE_ACTIVE;
15075       else if (new_element == EL_EM_DYNAMITE)
15076         new_element = EL_EM_DYNAMITE_ACTIVE;
15077       else if (new_element == EL_SP_DISK_RED)
15078         new_element = EL_SP_DISK_RED_ACTIVE;
15079     }
15080
15081     Tile[dropx][dropy] = new_element;
15082
15083     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15084       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15085                           el2img(Tile[dropx][dropy]), 0);
15086
15087     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15088
15089     // needed if previous element just changed to "empty" in the last frame
15090     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
15091
15092     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15093                                player->index_bit, drop_side);
15094     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15095                                         CE_PLAYER_DROPS_X,
15096                                         player->index_bit, drop_side);
15097
15098     TestIfElementTouchesCustomElement(dropx, dropy);
15099   }
15100   else          // player is dropping a dyna bomb
15101   {
15102     player->dynabombs_left--;
15103
15104     Tile[dropx][dropy] = new_element;
15105
15106     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15107       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15108                           el2img(Tile[dropx][dropy]), 0);
15109
15110     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15111   }
15112
15113   if (Tile[dropx][dropy] == new_element) // uninitialized unless CE change
15114     InitField_WithBug1(dropx, dropy, FALSE);
15115
15116   new_element = Tile[dropx][dropy];     // element might have changed
15117
15118   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
15119       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
15120   {
15121     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
15122       MovDir[dropx][dropy] = drop_direction;
15123
15124     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
15125
15126     // do not cause impact style collision by dropping elements that can fall
15127     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15128   }
15129
15130   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
15131   player->is_dropping = TRUE;
15132
15133   player->drop_pressed_delay = 0;
15134   player->is_dropping_pressed = FALSE;
15135
15136   player->drop_x = dropx;
15137   player->drop_y = dropy;
15138
15139   return TRUE;
15140 }
15141
15142 // ----------------------------------------------------------------------------
15143 // game sound playing functions
15144 // ----------------------------------------------------------------------------
15145
15146 static int *loop_sound_frame = NULL;
15147 static int *loop_sound_volume = NULL;
15148
15149 void InitPlayLevelSound(void)
15150 {
15151   int num_sounds = getSoundListSize();
15152
15153   checked_free(loop_sound_frame);
15154   checked_free(loop_sound_volume);
15155
15156   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
15157   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
15158 }
15159
15160 static void PlayLevelSound(int x, int y, int nr)
15161 {
15162   int sx = SCREENX(x), sy = SCREENY(y);
15163   int volume, stereo_position;
15164   int max_distance = 8;
15165   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
15166
15167   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
15168       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
15169     return;
15170
15171   if (!IN_LEV_FIELD(x, y) ||
15172       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
15173       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
15174     return;
15175
15176   volume = SOUND_MAX_VOLUME;
15177
15178   if (!IN_SCR_FIELD(sx, sy))
15179   {
15180     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
15181     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
15182
15183     volume -= volume * (dx > dy ? dx : dy) / max_distance;
15184   }
15185
15186   stereo_position = (SOUND_MAX_LEFT +
15187                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
15188                      (SCR_FIELDX + 2 * max_distance));
15189
15190   if (IS_LOOP_SOUND(nr))
15191   {
15192     /* This assures that quieter loop sounds do not overwrite louder ones,
15193        while restarting sound volume comparison with each new game frame. */
15194
15195     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
15196       return;
15197
15198     loop_sound_volume[nr] = volume;
15199     loop_sound_frame[nr] = FrameCounter;
15200   }
15201
15202   PlaySoundExt(nr, volume, stereo_position, type);
15203 }
15204
15205 static void PlayLevelSoundNearest(int x, int y, int sound_action)
15206 {
15207   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
15208                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
15209                  y < LEVELY(BY1) ? LEVELY(BY1) :
15210                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
15211                  sound_action);
15212 }
15213
15214 static void PlayLevelSoundAction(int x, int y, int action)
15215 {
15216   PlayLevelSoundElementAction(x, y, Tile[x][y], action);
15217 }
15218
15219 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
15220 {
15221   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15222
15223   if (sound_effect != SND_UNDEFINED)
15224     PlayLevelSound(x, y, sound_effect);
15225 }
15226
15227 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
15228                                               int action)
15229 {
15230   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15231
15232   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15233     PlayLevelSound(x, y, sound_effect);
15234 }
15235
15236 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
15237 {
15238   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15239
15240   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15241     PlayLevelSound(x, y, sound_effect);
15242 }
15243
15244 static void StopLevelSoundActionIfLoop(int x, int y, int action)
15245 {
15246   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15247
15248   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15249     StopSound(sound_effect);
15250 }
15251
15252 static int getLevelMusicNr(void)
15253 {
15254   int level_pos = level_nr - leveldir_current->first_level;
15255
15256   if (levelset.music[level_nr] != MUS_UNDEFINED)
15257     return levelset.music[level_nr];            // from config file
15258   else
15259     return MAP_NOCONF_MUSIC(level_pos);         // from music dir
15260 }
15261
15262 static void FadeLevelSounds(void)
15263 {
15264   FadeSounds();
15265 }
15266
15267 static void FadeLevelMusic(void)
15268 {
15269   int music_nr = getLevelMusicNr();
15270   char *curr_music = getCurrentlyPlayingMusicFilename();
15271   char *next_music = getMusicInfoEntryFilename(music_nr);
15272
15273   if (!strEqual(curr_music, next_music))
15274     FadeMusic();
15275 }
15276
15277 void FadeLevelSoundsAndMusic(void)
15278 {
15279   FadeLevelSounds();
15280   FadeLevelMusic();
15281 }
15282
15283 static void PlayLevelMusic(void)
15284 {
15285   int music_nr = getLevelMusicNr();
15286   char *curr_music = getCurrentlyPlayingMusicFilename();
15287   char *next_music = getMusicInfoEntryFilename(music_nr);
15288
15289   if (!strEqual(curr_music, next_music))
15290     PlayMusicLoop(music_nr);
15291 }
15292
15293 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15294 {
15295   int element = (element_em > -1 ? map_element_EM_to_RND_game(element_em) : 0);
15296   int offset = 0;
15297   int x = xx - offset;
15298   int y = yy - offset;
15299
15300   switch (sample)
15301   {
15302     case SOUND_blank:
15303       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15304       break;
15305
15306     case SOUND_roll:
15307       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15308       break;
15309
15310     case SOUND_stone:
15311       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15312       break;
15313
15314     case SOUND_nut:
15315       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15316       break;
15317
15318     case SOUND_crack:
15319       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15320       break;
15321
15322     case SOUND_bug:
15323       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15324       break;
15325
15326     case SOUND_tank:
15327       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15328       break;
15329
15330     case SOUND_android_clone:
15331       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15332       break;
15333
15334     case SOUND_android_move:
15335       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15336       break;
15337
15338     case SOUND_spring:
15339       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15340       break;
15341
15342     case SOUND_slurp:
15343       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15344       break;
15345
15346     case SOUND_eater:
15347       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15348       break;
15349
15350     case SOUND_eater_eat:
15351       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15352       break;
15353
15354     case SOUND_alien:
15355       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15356       break;
15357
15358     case SOUND_collect:
15359       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15360       break;
15361
15362     case SOUND_diamond:
15363       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15364       break;
15365
15366     case SOUND_squash:
15367       // !!! CHECK THIS !!!
15368 #if 1
15369       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15370 #else
15371       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15372 #endif
15373       break;
15374
15375     case SOUND_wonderfall:
15376       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15377       break;
15378
15379     case SOUND_drip:
15380       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15381       break;
15382
15383     case SOUND_push:
15384       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15385       break;
15386
15387     case SOUND_dirt:
15388       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15389       break;
15390
15391     case SOUND_acid:
15392       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15393       break;
15394
15395     case SOUND_ball:
15396       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15397       break;
15398
15399     case SOUND_slide:
15400       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15401       break;
15402
15403     case SOUND_wonder:
15404       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15405       break;
15406
15407     case SOUND_door:
15408       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15409       break;
15410
15411     case SOUND_exit_open:
15412       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15413       break;
15414
15415     case SOUND_exit_leave:
15416       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15417       break;
15418
15419     case SOUND_dynamite:
15420       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15421       break;
15422
15423     case SOUND_tick:
15424       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15425       break;
15426
15427     case SOUND_press:
15428       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15429       break;
15430
15431     case SOUND_wheel:
15432       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15433       break;
15434
15435     case SOUND_boom:
15436       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15437       break;
15438
15439     case SOUND_die:
15440       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15441       break;
15442
15443     case SOUND_time:
15444       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15445       break;
15446
15447     default:
15448       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15449       break;
15450   }
15451 }
15452
15453 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
15454 {
15455   int element = map_element_SP_to_RND(element_sp);
15456   int action = map_action_SP_to_RND(action_sp);
15457   int offset = (setup.sp_show_border_elements ? 0 : 1);
15458   int x = xx - offset;
15459   int y = yy - offset;
15460
15461   PlayLevelSoundElementAction(x, y, element, action);
15462 }
15463
15464 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
15465 {
15466   int element = map_element_MM_to_RND(element_mm);
15467   int action = map_action_MM_to_RND(action_mm);
15468   int offset = 0;
15469   int x = xx - offset;
15470   int y = yy - offset;
15471
15472   if (!IS_MM_ELEMENT(element))
15473     element = EL_MM_DEFAULT;
15474
15475   PlayLevelSoundElementAction(x, y, element, action);
15476 }
15477
15478 void PlaySound_MM(int sound_mm)
15479 {
15480   int sound = map_sound_MM_to_RND(sound_mm);
15481
15482   if (sound == SND_UNDEFINED)
15483     return;
15484
15485   PlaySound(sound);
15486 }
15487
15488 void PlaySoundLoop_MM(int sound_mm)
15489 {
15490   int sound = map_sound_MM_to_RND(sound_mm);
15491
15492   if (sound == SND_UNDEFINED)
15493     return;
15494
15495   PlaySoundLoop(sound);
15496 }
15497
15498 void StopSound_MM(int sound_mm)
15499 {
15500   int sound = map_sound_MM_to_RND(sound_mm);
15501
15502   if (sound == SND_UNDEFINED)
15503     return;
15504
15505   StopSound(sound);
15506 }
15507
15508 void RaiseScore(int value)
15509 {
15510   game.score += value;
15511
15512   game_panel_controls[GAME_PANEL_SCORE].value = game.score;
15513
15514   DisplayGameControlValues();
15515 }
15516
15517 void RaiseScoreElement(int element)
15518 {
15519   switch (element)
15520   {
15521     case EL_EMERALD:
15522     case EL_BD_DIAMOND:
15523     case EL_EMERALD_YELLOW:
15524     case EL_EMERALD_RED:
15525     case EL_EMERALD_PURPLE:
15526     case EL_SP_INFOTRON:
15527       RaiseScore(level.score[SC_EMERALD]);
15528       break;
15529     case EL_DIAMOND:
15530       RaiseScore(level.score[SC_DIAMOND]);
15531       break;
15532     case EL_CRYSTAL:
15533       RaiseScore(level.score[SC_CRYSTAL]);
15534       break;
15535     case EL_PEARL:
15536       RaiseScore(level.score[SC_PEARL]);
15537       break;
15538     case EL_BUG:
15539     case EL_BD_BUTTERFLY:
15540     case EL_SP_ELECTRON:
15541       RaiseScore(level.score[SC_BUG]);
15542       break;
15543     case EL_SPACESHIP:
15544     case EL_BD_FIREFLY:
15545     case EL_SP_SNIKSNAK:
15546       RaiseScore(level.score[SC_SPACESHIP]);
15547       break;
15548     case EL_YAMYAM:
15549     case EL_DARK_YAMYAM:
15550       RaiseScore(level.score[SC_YAMYAM]);
15551       break;
15552     case EL_ROBOT:
15553       RaiseScore(level.score[SC_ROBOT]);
15554       break;
15555     case EL_PACMAN:
15556       RaiseScore(level.score[SC_PACMAN]);
15557       break;
15558     case EL_NUT:
15559       RaiseScore(level.score[SC_NUT]);
15560       break;
15561     case EL_DYNAMITE:
15562     case EL_EM_DYNAMITE:
15563     case EL_SP_DISK_RED:
15564     case EL_DYNABOMB_INCREASE_NUMBER:
15565     case EL_DYNABOMB_INCREASE_SIZE:
15566     case EL_DYNABOMB_INCREASE_POWER:
15567       RaiseScore(level.score[SC_DYNAMITE]);
15568       break;
15569     case EL_SHIELD_NORMAL:
15570     case EL_SHIELD_DEADLY:
15571       RaiseScore(level.score[SC_SHIELD]);
15572       break;
15573     case EL_EXTRA_TIME:
15574       RaiseScore(level.extra_time_score);
15575       break;
15576     case EL_KEY_1:
15577     case EL_KEY_2:
15578     case EL_KEY_3:
15579     case EL_KEY_4:
15580     case EL_EM_KEY_1:
15581     case EL_EM_KEY_2:
15582     case EL_EM_KEY_3:
15583     case EL_EM_KEY_4:
15584     case EL_EMC_KEY_5:
15585     case EL_EMC_KEY_6:
15586     case EL_EMC_KEY_7:
15587     case EL_EMC_KEY_8:
15588     case EL_DC_KEY_WHITE:
15589       RaiseScore(level.score[SC_KEY]);
15590       break;
15591     default:
15592       RaiseScore(element_info[element].collect_score);
15593       break;
15594   }
15595 }
15596
15597 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15598 {
15599   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15600   {
15601     if (!quick_quit)
15602     {
15603       // prevent short reactivation of overlay buttons while closing door
15604       SetOverlayActive(FALSE);
15605       UnmapGameButtons();
15606
15607       // door may still be open due to skipped or envelope style request
15608       CloseDoor(score_info_tape_play ? DOOR_CLOSE_ALL : DOOR_CLOSE_1);
15609     }
15610
15611     if (network.enabled)
15612       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15613     else
15614     {
15615       if (quick_quit)
15616         FadeSkipNextFadeIn();
15617
15618       SetGameStatus(GAME_MODE_MAIN);
15619
15620       DrawMainMenu();
15621     }
15622   }
15623   else          // continue playing the game
15624   {
15625     if (tape.playing && tape.deactivate_display)
15626       TapeDeactivateDisplayOff(TRUE);
15627
15628     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15629
15630     if (tape.playing && tape.deactivate_display)
15631       TapeDeactivateDisplayOn();
15632   }
15633 }
15634
15635 void RequestQuitGame(boolean escape_key_pressed)
15636 {
15637   boolean ask_on_escape = (setup.ask_on_escape && setup.ask_on_quit_game);
15638   boolean quick_quit = ((escape_key_pressed && !ask_on_escape) ||
15639                         level_editor_test_game);
15640   boolean skip_request = (game.all_players_gone || !setup.ask_on_quit_game ||
15641                           quick_quit || score_info_tape_play);
15642
15643   RequestQuitGameExt(skip_request, quick_quit,
15644                      "Do you really want to quit the game?");
15645 }
15646
15647 static char *getRestartGameMessage(void)
15648 {
15649   boolean play_again = hasStartedNetworkGame();
15650   static char message[MAX_OUTPUT_LINESIZE];
15651   char *game_over_text = "Game over!";
15652   char *play_again_text = " Play it again?";
15653
15654   if (level.game_engine_type == GAME_ENGINE_TYPE_MM &&
15655       game_mm.game_over_message != NULL)
15656     game_over_text = game_mm.game_over_message;
15657
15658   snprintf(message, MAX_OUTPUT_LINESIZE, "%s%s", game_over_text,
15659            (play_again ? play_again_text : ""));
15660
15661   return message;
15662 }
15663
15664 static void RequestRestartGame(void)
15665 {
15666   char *message = getRestartGameMessage();
15667   boolean has_started_game = hasStartedNetworkGame();
15668   int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM);
15669   int door_state = DOOR_CLOSE_1;
15670
15671   if (Request(message, request_mode | REQ_STAY_OPEN) && has_started_game)
15672   {
15673     CloseDoor(door_state);
15674
15675     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15676   }
15677   else
15678   {
15679     // if game was invoked from level editor, also close tape recorder door
15680     if (level_editor_test_game)
15681       door_state = DOOR_CLOSE_ALL;
15682
15683     CloseDoor(door_state);
15684
15685     SetGameStatus(GAME_MODE_MAIN);
15686
15687     DrawMainMenu();
15688   }
15689 }
15690
15691 boolean CheckRestartGame(void)
15692 {
15693   static int game_over_delay = 0;
15694   int game_over_delay_value = 50;
15695   boolean game_over = checkGameFailed();
15696
15697   if (!game_over)
15698   {
15699     game_over_delay = game_over_delay_value;
15700
15701     return FALSE;
15702   }
15703
15704   if (game_over_delay > 0)
15705   {
15706     if (game_over_delay == game_over_delay_value / 2)
15707       PlaySound(SND_GAME_LOSING);
15708
15709     game_over_delay--;
15710
15711     return FALSE;
15712   }
15713
15714   // do not handle game over if request dialog is already active
15715   if (game.request_active)
15716     return FALSE;
15717
15718   // do not ask to play again if game was never actually played
15719   if (!game.GamePlayed)
15720     return FALSE;
15721
15722   // do not ask to play again if this was disabled in setup menu
15723   if (!setup.ask_on_game_over)
15724     return FALSE;
15725
15726   RequestRestartGame();
15727
15728   return TRUE;
15729 }
15730
15731 boolean checkGameSolved(void)
15732 {
15733   // set for all game engines if level was solved
15734   return game.LevelSolved_GameEnd;
15735 }
15736
15737 boolean checkGameFailed(void)
15738 {
15739   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15740     return (game_em.game_over && !game_em.level_solved);
15741   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15742     return (game_sp.game_over && !game_sp.level_solved);
15743   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15744     return (game_mm.game_over && !game_mm.level_solved);
15745   else                          // GAME_ENGINE_TYPE_RND
15746     return (game.GameOver && !game.LevelSolved);
15747 }
15748
15749 boolean checkGameEnded(void)
15750 {
15751   return (checkGameSolved() || checkGameFailed());
15752 }
15753
15754
15755 // ----------------------------------------------------------------------------
15756 // random generator functions
15757 // ----------------------------------------------------------------------------
15758
15759 unsigned int InitEngineRandom_RND(int seed)
15760 {
15761   game.num_random_calls = 0;
15762
15763   return InitEngineRandom(seed);
15764 }
15765
15766 unsigned int RND(int max)
15767 {
15768   if (max > 0)
15769   {
15770     game.num_random_calls++;
15771
15772     return GetEngineRandom(max);
15773   }
15774
15775   return 0;
15776 }
15777
15778
15779 // ----------------------------------------------------------------------------
15780 // game engine snapshot handling functions
15781 // ----------------------------------------------------------------------------
15782
15783 struct EngineSnapshotInfo
15784 {
15785   // runtime values for custom element collect score
15786   int collect_score[NUM_CUSTOM_ELEMENTS];
15787
15788   // runtime values for group element choice position
15789   int choice_pos[NUM_GROUP_ELEMENTS];
15790
15791   // runtime values for belt position animations
15792   int belt_graphic[4][NUM_BELT_PARTS];
15793   int belt_anim_mode[4][NUM_BELT_PARTS];
15794 };
15795
15796 static struct EngineSnapshotInfo engine_snapshot_rnd;
15797 static char *snapshot_level_identifier = NULL;
15798 static int snapshot_level_nr = -1;
15799
15800 static void SaveEngineSnapshotValues_RND(void)
15801 {
15802   static int belt_base_active_element[4] =
15803   {
15804     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15805     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15806     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15807     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15808   };
15809   int i, j;
15810
15811   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15812   {
15813     int element = EL_CUSTOM_START + i;
15814
15815     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15816   }
15817
15818   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15819   {
15820     int element = EL_GROUP_START + i;
15821
15822     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15823   }
15824
15825   for (i = 0; i < 4; i++)
15826   {
15827     for (j = 0; j < NUM_BELT_PARTS; j++)
15828     {
15829       int element = belt_base_active_element[i] + j;
15830       int graphic = el2img(element);
15831       int anim_mode = graphic_info[graphic].anim_mode;
15832
15833       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15834       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15835     }
15836   }
15837 }
15838
15839 static void LoadEngineSnapshotValues_RND(void)
15840 {
15841   unsigned int num_random_calls = game.num_random_calls;
15842   int i, j;
15843
15844   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15845   {
15846     int element = EL_CUSTOM_START + i;
15847
15848     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15849   }
15850
15851   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15852   {
15853     int element = EL_GROUP_START + i;
15854
15855     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15856   }
15857
15858   for (i = 0; i < 4; i++)
15859   {
15860     for (j = 0; j < NUM_BELT_PARTS; j++)
15861     {
15862       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15863       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15864
15865       graphic_info[graphic].anim_mode = anim_mode;
15866     }
15867   }
15868
15869   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15870   {
15871     InitRND(tape.random_seed);
15872     for (i = 0; i < num_random_calls; i++)
15873       RND(1);
15874   }
15875
15876   if (game.num_random_calls != num_random_calls)
15877   {
15878     Error("number of random calls out of sync");
15879     Error("number of random calls should be %d", num_random_calls);
15880     Error("number of random calls is %d", game.num_random_calls);
15881
15882     Fail("this should not happen -- please debug");
15883   }
15884 }
15885
15886 void FreeEngineSnapshotSingle(void)
15887 {
15888   FreeSnapshotSingle();
15889
15890   setString(&snapshot_level_identifier, NULL);
15891   snapshot_level_nr = -1;
15892 }
15893
15894 void FreeEngineSnapshotList(void)
15895 {
15896   FreeSnapshotList();
15897 }
15898
15899 static ListNode *SaveEngineSnapshotBuffers(void)
15900 {
15901   ListNode *buffers = NULL;
15902
15903   // copy some special values to a structure better suited for the snapshot
15904
15905   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15906     SaveEngineSnapshotValues_RND();
15907   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15908     SaveEngineSnapshotValues_EM();
15909   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15910     SaveEngineSnapshotValues_SP(&buffers);
15911   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15912     SaveEngineSnapshotValues_MM();
15913
15914   // save values stored in special snapshot structure
15915
15916   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15917     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15918   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15919     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15920   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15921     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15922   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15923     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15924
15925   // save further RND engine values
15926
15927   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15928   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15929   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15930
15931   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15932   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15933   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15934   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15935   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15936
15937   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15938   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15939   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15940
15941   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15942
15943   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15944   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15945
15946   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Tile));
15947   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15948   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15949   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15950   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15951   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15952   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15953   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15954   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15955   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15956   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15957   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15958   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15959   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15960   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15961   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15962   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15963   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15964
15965   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15966   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15967
15968   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15969   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15970   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15971
15972   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15973   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15974
15975   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15976   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15977   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandomStatic));
15978   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15979   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15980   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15981
15982   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15983   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15984
15985 #if 0
15986   ListNode *node = engine_snapshot_list_rnd;
15987   int num_bytes = 0;
15988
15989   while (node != NULL)
15990   {
15991     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15992
15993     node = node->next;
15994   }
15995
15996   Debug("game:playing:SaveEngineSnapshotBuffers",
15997         "size of engine snapshot: %d bytes", num_bytes);
15998 #endif
15999
16000   return buffers;
16001 }
16002
16003 void SaveEngineSnapshotSingle(void)
16004 {
16005   ListNode *buffers = SaveEngineSnapshotBuffers();
16006
16007   // finally save all snapshot buffers to single snapshot
16008   SaveSnapshotSingle(buffers);
16009
16010   // save level identification information
16011   setString(&snapshot_level_identifier, leveldir_current->identifier);
16012   snapshot_level_nr = level_nr;
16013 }
16014
16015 boolean CheckSaveEngineSnapshotToList(void)
16016 {
16017   boolean save_snapshot =
16018     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
16019      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
16020       game.snapshot.changed_action) ||
16021      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16022       game.snapshot.collected_item));
16023
16024   game.snapshot.changed_action = FALSE;
16025   game.snapshot.collected_item = FALSE;
16026   game.snapshot.save_snapshot = save_snapshot;
16027
16028   return save_snapshot;
16029 }
16030
16031 void SaveEngineSnapshotToList(void)
16032 {
16033   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
16034       tape.quick_resume)
16035     return;
16036
16037   ListNode *buffers = SaveEngineSnapshotBuffers();
16038
16039   // finally save all snapshot buffers to snapshot list
16040   SaveSnapshotToList(buffers);
16041 }
16042
16043 void SaveEngineSnapshotToListInitial(void)
16044 {
16045   FreeEngineSnapshotList();
16046
16047   SaveEngineSnapshotToList();
16048 }
16049
16050 static void LoadEngineSnapshotValues(void)
16051 {
16052   // restore special values from snapshot structure
16053
16054   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16055     LoadEngineSnapshotValues_RND();
16056   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
16057     LoadEngineSnapshotValues_EM();
16058   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
16059     LoadEngineSnapshotValues_SP();
16060   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
16061     LoadEngineSnapshotValues_MM();
16062 }
16063
16064 void LoadEngineSnapshotSingle(void)
16065 {
16066   LoadSnapshotSingle();
16067
16068   LoadEngineSnapshotValues();
16069 }
16070
16071 static void LoadEngineSnapshot_Undo(int steps)
16072 {
16073   LoadSnapshotFromList_Older(steps);
16074
16075   LoadEngineSnapshotValues();
16076 }
16077
16078 static void LoadEngineSnapshot_Redo(int steps)
16079 {
16080   LoadSnapshotFromList_Newer(steps);
16081
16082   LoadEngineSnapshotValues();
16083 }
16084
16085 boolean CheckEngineSnapshotSingle(void)
16086 {
16087   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16088           snapshot_level_nr == level_nr);
16089 }
16090
16091 boolean CheckEngineSnapshotList(void)
16092 {
16093   return CheckSnapshotList();
16094 }
16095
16096
16097 // ---------- new game button stuff -------------------------------------------
16098
16099 static struct
16100 {
16101   int graphic;
16102   struct XY *pos;
16103   int gadget_id;
16104   boolean *setup_value;
16105   boolean allowed_on_tape;
16106   boolean is_touch_button;
16107   char *infotext;
16108 } gamebutton_info[NUM_GAME_BUTTONS] =
16109 {
16110   {
16111     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
16112     GAME_CTRL_ID_STOP,                          NULL,
16113     TRUE, FALSE,                                "stop game"
16114   },
16115   {
16116     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
16117     GAME_CTRL_ID_PAUSE,                         NULL,
16118     TRUE, FALSE,                                "pause game"
16119   },
16120   {
16121     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
16122     GAME_CTRL_ID_PLAY,                          NULL,
16123     TRUE, FALSE,                                "play game"
16124   },
16125   {
16126     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
16127     GAME_CTRL_ID_UNDO,                          NULL,
16128     TRUE, FALSE,                                "undo step"
16129   },
16130   {
16131     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
16132     GAME_CTRL_ID_REDO,                          NULL,
16133     TRUE, FALSE,                                "redo step"
16134   },
16135   {
16136     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
16137     GAME_CTRL_ID_SAVE,                          NULL,
16138     TRUE, FALSE,                                "save game"
16139   },
16140   {
16141     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
16142     GAME_CTRL_ID_PAUSE2,                        NULL,
16143     TRUE, FALSE,                                "pause game"
16144   },
16145   {
16146     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
16147     GAME_CTRL_ID_LOAD,                          NULL,
16148     TRUE, FALSE,                                "load game"
16149   },
16150   {
16151     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
16152     GAME_CTRL_ID_PANEL_STOP,                    NULL,
16153     FALSE, FALSE,                               "stop game"
16154   },
16155   {
16156     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
16157     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
16158     FALSE, FALSE,                               "pause game"
16159   },
16160   {
16161     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
16162     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
16163     FALSE, FALSE,                               "play game"
16164   },
16165   {
16166     IMG_GFX_GAME_BUTTON_TOUCH_STOP,             &game.button.touch_stop,
16167     GAME_CTRL_ID_TOUCH_STOP,                    NULL,
16168     FALSE, TRUE,                                "stop game"
16169   },
16170   {
16171     IMG_GFX_GAME_BUTTON_TOUCH_PAUSE,            &game.button.touch_pause,
16172     GAME_CTRL_ID_TOUCH_PAUSE,                   NULL,
16173     FALSE, TRUE,                                "pause game"
16174   },
16175   {
16176     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
16177     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
16178     TRUE, FALSE,                                "background music on/off"
16179   },
16180   {
16181     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
16182     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
16183     TRUE, FALSE,                                "sound loops on/off"
16184   },
16185   {
16186     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
16187     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
16188     TRUE, FALSE,                                "normal sounds on/off"
16189   },
16190   {
16191     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
16192     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
16193     FALSE, FALSE,                               "background music on/off"
16194   },
16195   {
16196     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
16197     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
16198     FALSE, FALSE,                               "sound loops on/off"
16199   },
16200   {
16201     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
16202     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
16203     FALSE, FALSE,                               "normal sounds on/off"
16204   }
16205 };
16206
16207 void CreateGameButtons(void)
16208 {
16209   int i;
16210
16211   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16212   {
16213     int graphic = gamebutton_info[i].graphic;
16214     struct GraphicInfo *gfx = &graphic_info[graphic];
16215     struct XY *pos = gamebutton_info[i].pos;
16216     struct GadgetInfo *gi;
16217     int button_type;
16218     boolean checked;
16219     unsigned int event_mask;
16220     boolean is_touch_button = gamebutton_info[i].is_touch_button;
16221     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
16222     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
16223     int base_x = (is_touch_button ? 0 : on_tape ? VX : DX);
16224     int base_y = (is_touch_button ? 0 : on_tape ? VY : DY);
16225     int gd_x   = gfx->src_x;
16226     int gd_y   = gfx->src_y;
16227     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
16228     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
16229     int gd_xa  = gfx->src_x + gfx->active_xoffset;
16230     int gd_ya  = gfx->src_y + gfx->active_yoffset;
16231     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
16232     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
16233     int x = (is_touch_button ? pos->x : GDI_ACTIVE_POS(pos->x));
16234     int y = (is_touch_button ? pos->y : GDI_ACTIVE_POS(pos->y));
16235     int id = i;
16236
16237     // do not use touch buttons if overlay touch buttons are disabled
16238     if (is_touch_button && !setup.touch.overlay_buttons)
16239       continue;
16240
16241     if (gfx->bitmap == NULL)
16242     {
16243       game_gadget[id] = NULL;
16244
16245       continue;
16246     }
16247
16248     if (id == GAME_CTRL_ID_STOP ||
16249         id == GAME_CTRL_ID_PANEL_STOP ||
16250         id == GAME_CTRL_ID_TOUCH_STOP ||
16251         id == GAME_CTRL_ID_PLAY ||
16252         id == GAME_CTRL_ID_PANEL_PLAY ||
16253         id == GAME_CTRL_ID_SAVE ||
16254         id == GAME_CTRL_ID_LOAD)
16255     {
16256       button_type = GD_TYPE_NORMAL_BUTTON;
16257       checked = FALSE;
16258       event_mask = GD_EVENT_RELEASED;
16259     }
16260     else if (id == GAME_CTRL_ID_UNDO ||
16261              id == GAME_CTRL_ID_REDO)
16262     {
16263       button_type = GD_TYPE_NORMAL_BUTTON;
16264       checked = FALSE;
16265       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
16266     }
16267     else
16268     {
16269       button_type = GD_TYPE_CHECK_BUTTON;
16270       checked = (gamebutton_info[i].setup_value != NULL ?
16271                  *gamebutton_info[i].setup_value : FALSE);
16272       event_mask = GD_EVENT_PRESSED;
16273     }
16274
16275     gi = CreateGadget(GDI_CUSTOM_ID, id,
16276                       GDI_IMAGE_ID, graphic,
16277                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
16278                       GDI_X, base_x + x,
16279                       GDI_Y, base_y + y,
16280                       GDI_WIDTH, gfx->width,
16281                       GDI_HEIGHT, gfx->height,
16282                       GDI_TYPE, button_type,
16283                       GDI_STATE, GD_BUTTON_UNPRESSED,
16284                       GDI_CHECKED, checked,
16285                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
16286                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
16287                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
16288                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
16289                       GDI_DIRECT_DRAW, FALSE,
16290                       GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
16291                       GDI_EVENT_MASK, event_mask,
16292                       GDI_CALLBACK_ACTION, HandleGameButtons,
16293                       GDI_END);
16294
16295     if (gi == NULL)
16296       Fail("cannot create gadget");
16297
16298     game_gadget[id] = gi;
16299   }
16300 }
16301
16302 void FreeGameButtons(void)
16303 {
16304   int i;
16305
16306   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16307     FreeGadget(game_gadget[i]);
16308 }
16309
16310 static void UnmapGameButtonsAtSamePosition(int id)
16311 {
16312   int i;
16313
16314   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16315     if (i != id &&
16316         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
16317         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
16318       UnmapGadget(game_gadget[i]);
16319 }
16320
16321 static void UnmapGameButtonsAtSamePosition_All(void)
16322 {
16323   if (setup.show_load_save_buttons)
16324   {
16325     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16326     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16327     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16328   }
16329   else if (setup.show_undo_redo_buttons)
16330   {
16331     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16332     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16333     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16334   }
16335   else
16336   {
16337     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
16338     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
16339     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
16340
16341     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
16342     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
16343     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
16344   }
16345 }
16346
16347 void MapLoadSaveButtons(void)
16348 {
16349   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16350   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16351
16352   MapGadget(game_gadget[GAME_CTRL_ID_LOAD]);
16353   MapGadget(game_gadget[GAME_CTRL_ID_SAVE]);
16354 }
16355
16356 void MapUndoRedoButtons(void)
16357 {
16358   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16359   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16360
16361   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
16362   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
16363 }
16364
16365 void ModifyPauseButtons(void)
16366 {
16367   static int ids[] =
16368   {
16369     GAME_CTRL_ID_PAUSE,
16370     GAME_CTRL_ID_PAUSE2,
16371     GAME_CTRL_ID_PANEL_PAUSE,
16372     GAME_CTRL_ID_TOUCH_PAUSE,
16373     -1
16374   };
16375   int i;
16376
16377   for (i = 0; ids[i] > -1; i++)
16378     ModifyGadget(game_gadget[ids[i]], GDI_CHECKED, tape.pausing, GDI_END);
16379 }
16380
16381 static void MapGameButtonsExt(boolean on_tape)
16382 {
16383   int i;
16384
16385   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16386   {
16387     if ((i == GAME_CTRL_ID_UNDO ||
16388          i == GAME_CTRL_ID_REDO) &&
16389         game_status != GAME_MODE_PLAYING)
16390       continue;
16391
16392     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16393       MapGadget(game_gadget[i]);
16394   }
16395
16396   UnmapGameButtonsAtSamePosition_All();
16397
16398   RedrawGameButtons();
16399 }
16400
16401 static void UnmapGameButtonsExt(boolean on_tape)
16402 {
16403   int i;
16404
16405   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16406     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16407       UnmapGadget(game_gadget[i]);
16408 }
16409
16410 static void RedrawGameButtonsExt(boolean on_tape)
16411 {
16412   int i;
16413
16414   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16415     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16416       RedrawGadget(game_gadget[i]);
16417 }
16418
16419 static void SetGadgetState(struct GadgetInfo *gi, boolean state)
16420 {
16421   if (gi == NULL)
16422     return;
16423
16424   gi->checked = state;
16425 }
16426
16427 static void RedrawSoundButtonGadget(int id)
16428 {
16429   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
16430              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
16431              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
16432              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
16433              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
16434              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
16435              id);
16436
16437   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
16438   RedrawGadget(game_gadget[id2]);
16439 }
16440
16441 void MapGameButtons(void)
16442 {
16443   MapGameButtonsExt(FALSE);
16444 }
16445
16446 void UnmapGameButtons(void)
16447 {
16448   UnmapGameButtonsExt(FALSE);
16449 }
16450
16451 void RedrawGameButtons(void)
16452 {
16453   RedrawGameButtonsExt(FALSE);
16454 }
16455
16456 void MapGameButtonsOnTape(void)
16457 {
16458   MapGameButtonsExt(TRUE);
16459 }
16460
16461 void UnmapGameButtonsOnTape(void)
16462 {
16463   UnmapGameButtonsExt(TRUE);
16464 }
16465
16466 void RedrawGameButtonsOnTape(void)
16467 {
16468   RedrawGameButtonsExt(TRUE);
16469 }
16470
16471 static void GameUndoRedoExt(void)
16472 {
16473   ClearPlayerAction();
16474
16475   tape.pausing = TRUE;
16476
16477   RedrawPlayfield();
16478   UpdateAndDisplayGameControlValues();
16479
16480   DrawCompleteVideoDisplay();
16481   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
16482   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
16483   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
16484
16485   ModifyPauseButtons();
16486
16487   BackToFront();
16488 }
16489
16490 static void GameUndo(int steps)
16491 {
16492   if (!CheckEngineSnapshotList())
16493     return;
16494
16495   int tape_property_bits = tape.property_bits;
16496
16497   LoadEngineSnapshot_Undo(steps);
16498
16499   tape.property_bits |= tape_property_bits | TAPE_PROPERTY_SNAPSHOT;
16500
16501   GameUndoRedoExt();
16502 }
16503
16504 static void GameRedo(int steps)
16505 {
16506   if (!CheckEngineSnapshotList())
16507     return;
16508
16509   int tape_property_bits = tape.property_bits;
16510
16511   LoadEngineSnapshot_Redo(steps);
16512
16513   tape.property_bits |= tape_property_bits | TAPE_PROPERTY_SNAPSHOT;
16514
16515   GameUndoRedoExt();
16516 }
16517
16518 static void HandleGameButtonsExt(int id, int button)
16519 {
16520   static boolean game_undo_executed = FALSE;
16521   int steps = BUTTON_STEPSIZE(button);
16522   boolean handle_game_buttons =
16523     (game_status == GAME_MODE_PLAYING ||
16524      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
16525
16526   if (!handle_game_buttons)
16527     return;
16528
16529   switch (id)
16530   {
16531     case GAME_CTRL_ID_STOP:
16532     case GAME_CTRL_ID_PANEL_STOP:
16533     case GAME_CTRL_ID_TOUCH_STOP:
16534       TapeStopGame();
16535
16536       break;
16537
16538     case GAME_CTRL_ID_PAUSE:
16539     case GAME_CTRL_ID_PAUSE2:
16540     case GAME_CTRL_ID_PANEL_PAUSE:
16541     case GAME_CTRL_ID_TOUCH_PAUSE:
16542       if (network.enabled && game_status == GAME_MODE_PLAYING)
16543       {
16544         if (tape.pausing)
16545           SendToServer_ContinuePlaying();
16546         else
16547           SendToServer_PausePlaying();
16548       }
16549       else
16550         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16551
16552       game_undo_executed = FALSE;
16553
16554       break;
16555
16556     case GAME_CTRL_ID_PLAY:
16557     case GAME_CTRL_ID_PANEL_PLAY:
16558       if (game_status == GAME_MODE_MAIN)
16559       {
16560         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
16561       }
16562       else if (tape.pausing)
16563       {
16564         if (network.enabled)
16565           SendToServer_ContinuePlaying();
16566         else
16567           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
16568       }
16569       break;
16570
16571     case GAME_CTRL_ID_UNDO:
16572       // Important: When using "save snapshot when collecting an item" mode,
16573       // load last (current) snapshot for first "undo" after pressing "pause"
16574       // (else the last-but-one snapshot would be loaded, because the snapshot
16575       // pointer already points to the last snapshot when pressing "pause",
16576       // which is fine for "every step/move" mode, but not for "every collect")
16577       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16578           !game_undo_executed)
16579         steps--;
16580
16581       game_undo_executed = TRUE;
16582
16583       GameUndo(steps);
16584       break;
16585
16586     case GAME_CTRL_ID_REDO:
16587       GameRedo(steps);
16588       break;
16589
16590     case GAME_CTRL_ID_SAVE:
16591       TapeQuickSave();
16592       break;
16593
16594     case GAME_CTRL_ID_LOAD:
16595       TapeQuickLoad();
16596       break;
16597
16598     case SOUND_CTRL_ID_MUSIC:
16599     case SOUND_CTRL_ID_PANEL_MUSIC:
16600       if (setup.sound_music)
16601       { 
16602         setup.sound_music = FALSE;
16603
16604         FadeMusic();
16605       }
16606       else if (audio.music_available)
16607       { 
16608         setup.sound = setup.sound_music = TRUE;
16609
16610         SetAudioMode(setup.sound);
16611
16612         if (game_status == GAME_MODE_PLAYING)
16613           PlayLevelMusic();
16614       }
16615
16616       RedrawSoundButtonGadget(id);
16617
16618       break;
16619
16620     case SOUND_CTRL_ID_LOOPS:
16621     case SOUND_CTRL_ID_PANEL_LOOPS:
16622       if (setup.sound_loops)
16623         setup.sound_loops = FALSE;
16624       else if (audio.loops_available)
16625       {
16626         setup.sound = setup.sound_loops = TRUE;
16627
16628         SetAudioMode(setup.sound);
16629       }
16630
16631       RedrawSoundButtonGadget(id);
16632
16633       break;
16634
16635     case SOUND_CTRL_ID_SIMPLE:
16636     case SOUND_CTRL_ID_PANEL_SIMPLE:
16637       if (setup.sound_simple)
16638         setup.sound_simple = FALSE;
16639       else if (audio.sound_available)
16640       {
16641         setup.sound = setup.sound_simple = TRUE;
16642
16643         SetAudioMode(setup.sound);
16644       }
16645
16646       RedrawSoundButtonGadget(id);
16647
16648       break;
16649
16650     default:
16651       break;
16652   }
16653 }
16654
16655 static void HandleGameButtons(struct GadgetInfo *gi)
16656 {
16657   HandleGameButtonsExt(gi->custom_id, gi->event.button);
16658 }
16659
16660 void HandleSoundButtonKeys(Key key)
16661 {
16662   if (key == setup.shortcut.sound_simple)
16663     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
16664   else if (key == setup.shortcut.sound_loops)
16665     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
16666   else if (key == setup.shortcut.sound_music)
16667     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
16668 }