added optional button to restart game (door, panel and touch variants)
[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_RESTART            8
1021 #define GAME_CTRL_ID_PANEL_STOP         9
1022 #define GAME_CTRL_ID_PANEL_PAUSE        10
1023 #define GAME_CTRL_ID_PANEL_PLAY         11
1024 #define GAME_CTRL_ID_PANEL_RESTART      12
1025 #define GAME_CTRL_ID_TOUCH_STOP         13
1026 #define GAME_CTRL_ID_TOUCH_PAUSE        14
1027 #define GAME_CTRL_ID_TOUCH_RESTART      15
1028 #define SOUND_CTRL_ID_MUSIC             16
1029 #define SOUND_CTRL_ID_LOOPS             17
1030 #define SOUND_CTRL_ID_SIMPLE            18
1031 #define SOUND_CTRL_ID_PANEL_MUSIC       19
1032 #define SOUND_CTRL_ID_PANEL_LOOPS       20
1033 #define SOUND_CTRL_ID_PANEL_SIMPLE      21
1034
1035 #define NUM_GAME_BUTTONS                22
1036
1037
1038 // forward declaration for internal use
1039
1040 static void CreateField(int, int, int);
1041
1042 static void ResetGfxAnimation(int, int);
1043
1044 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1045 static void AdvanceFrameAndPlayerCounters(int);
1046
1047 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1048 static boolean MovePlayer(struct PlayerInfo *, int, int);
1049 static void ScrollPlayer(struct PlayerInfo *, int);
1050 static void ScrollScreen(struct PlayerInfo *, int);
1051
1052 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1053 static boolean DigFieldByCE(int, int, int);
1054 static boolean SnapField(struct PlayerInfo *, int, int);
1055 static boolean DropElement(struct PlayerInfo *);
1056
1057 static void InitBeltMovement(void);
1058 static void CloseAllOpenTimegates(void);
1059 static void CheckGravityMovement(struct PlayerInfo *);
1060 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1061 static void KillPlayerUnlessEnemyProtected(int, int);
1062 static void KillPlayerUnlessExplosionProtected(int, int);
1063
1064 static void CheckNextToConditions(int, int);
1065 static void TestIfPlayerNextToCustomElement(int, int);
1066 static void TestIfPlayerTouchesCustomElement(int, int);
1067 static void TestIfElementNextToCustomElement(int, int);
1068 static void TestIfElementTouchesCustomElement(int, int);
1069 static void TestIfElementHitsCustomElement(int, int, int);
1070
1071 static void HandleElementChange(int, int, int);
1072 static void ExecuteCustomElementAction(int, int, int, int);
1073 static boolean ChangeElement(int, int, int, int);
1074
1075 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int, int, int);
1076 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1077         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, -1)
1078 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1079         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1080 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1081         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1082 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1083         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1084 #define CheckTriggeredElementChangeByMouse(x, y, e, ev, s)              \
1085         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1086
1087 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1088 #define CheckElementChange(x, y, e, te, ev)                             \
1089         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1090 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1091         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1092 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1093         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1094 #define CheckElementChangeByMouse(x, y, e, ev, s)                       \
1095         CheckElementChangeExt(x, y, e, EL_UNDEFINED, ev, CH_PLAYER_ANY, s)
1096
1097 static void PlayLevelSound(int, int, int);
1098 static void PlayLevelSoundNearest(int, int, int);
1099 static void PlayLevelSoundAction(int, int, int);
1100 static void PlayLevelSoundElementAction(int, int, int, int);
1101 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1102 static void PlayLevelSoundActionIfLoop(int, int, int);
1103 static void StopLevelSoundActionIfLoop(int, int, int);
1104 static void PlayLevelMusic(void);
1105 static void FadeLevelSoundsAndMusic(void);
1106
1107 static void HandleGameButtons(struct GadgetInfo *);
1108
1109 int AmoebaNeighbourNr(int, int);
1110 void AmoebaToDiamond(int, int);
1111 void ContinueMoving(int, int);
1112 void Bang(int, int);
1113 void InitMovDir(int, int);
1114 void InitAmoebaNr(int, int);
1115 void NewHighScore(int, boolean);
1116
1117 void TestIfGoodThingHitsBadThing(int, int, int);
1118 void TestIfBadThingHitsGoodThing(int, int, int);
1119 void TestIfPlayerTouchesBadThing(int, int);
1120 void TestIfPlayerRunsIntoBadThing(int, int, int);
1121 void TestIfBadThingTouchesPlayer(int, int);
1122 void TestIfBadThingRunsIntoPlayer(int, int, int);
1123 void TestIfFriendTouchesBadThing(int, int);
1124 void TestIfBadThingTouchesFriend(int, int);
1125 void TestIfBadThingTouchesOtherBadThing(int, int);
1126 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1127
1128 void KillPlayer(struct PlayerInfo *);
1129 void BuryPlayer(struct PlayerInfo *);
1130 void RemovePlayer(struct PlayerInfo *);
1131 void ExitPlayer(struct PlayerInfo *);
1132
1133 static int getInvisibleActiveFromInvisibleElement(int);
1134 static int getInvisibleFromInvisibleActiveElement(int);
1135
1136 static void TestFieldAfterSnapping(int, int, int, int, int);
1137
1138 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1139
1140 // for detection of endless loops, caused by custom element programming
1141 // (using maximal playfield width x 10 is just a rough approximation)
1142 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1143
1144 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1145 {                                                                       \
1146   if (recursion_loop_detected)                                          \
1147     return (rc);                                                        \
1148                                                                         \
1149   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1150   {                                                                     \
1151     recursion_loop_detected = TRUE;                                     \
1152     recursion_loop_element = (e);                                       \
1153   }                                                                     \
1154                                                                         \
1155   recursion_loop_depth++;                                               \
1156 }
1157
1158 #define RECURSION_LOOP_DETECTION_END()                                  \
1159 {                                                                       \
1160   recursion_loop_depth--;                                               \
1161 }
1162
1163 static int recursion_loop_depth;
1164 static boolean recursion_loop_detected;
1165 static boolean recursion_loop_element;
1166
1167 static int map_player_action[MAX_PLAYERS];
1168
1169
1170 // ----------------------------------------------------------------------------
1171 // definition of elements that automatically change to other elements after
1172 // a specified time, eventually calling a function when changing
1173 // ----------------------------------------------------------------------------
1174
1175 // forward declaration for changer functions
1176 static void InitBuggyBase(int, int);
1177 static void WarnBuggyBase(int, int);
1178
1179 static void InitTrap(int, int);
1180 static void ActivateTrap(int, int);
1181 static void ChangeActiveTrap(int, int);
1182
1183 static void InitRobotWheel(int, int);
1184 static void RunRobotWheel(int, int);
1185 static void StopRobotWheel(int, int);
1186
1187 static void InitTimegateWheel(int, int);
1188 static void RunTimegateWheel(int, int);
1189
1190 static void InitMagicBallDelay(int, int);
1191 static void ActivateMagicBall(int, int);
1192
1193 struct ChangingElementInfo
1194 {
1195   int element;
1196   int target_element;
1197   int change_delay;
1198   void (*pre_change_function)(int x, int y);
1199   void (*change_function)(int x, int y);
1200   void (*post_change_function)(int x, int y);
1201 };
1202
1203 static struct ChangingElementInfo change_delay_list[] =
1204 {
1205   {
1206     EL_NUT_BREAKING,
1207     EL_EMERALD,
1208     6,
1209     NULL,
1210     NULL,
1211     NULL
1212   },
1213   {
1214     EL_PEARL_BREAKING,
1215     EL_EMPTY,
1216     8,
1217     NULL,
1218     NULL,
1219     NULL
1220   },
1221   {
1222     EL_EXIT_OPENING,
1223     EL_EXIT_OPEN,
1224     29,
1225     NULL,
1226     NULL,
1227     NULL
1228   },
1229   {
1230     EL_EXIT_CLOSING,
1231     EL_EXIT_CLOSED,
1232     29,
1233     NULL,
1234     NULL,
1235     NULL
1236   },
1237   {
1238     EL_STEEL_EXIT_OPENING,
1239     EL_STEEL_EXIT_OPEN,
1240     29,
1241     NULL,
1242     NULL,
1243     NULL
1244   },
1245   {
1246     EL_STEEL_EXIT_CLOSING,
1247     EL_STEEL_EXIT_CLOSED,
1248     29,
1249     NULL,
1250     NULL,
1251     NULL
1252   },
1253   {
1254     EL_EM_EXIT_OPENING,
1255     EL_EM_EXIT_OPEN,
1256     29,
1257     NULL,
1258     NULL,
1259     NULL
1260   },
1261   {
1262     EL_EM_EXIT_CLOSING,
1263     EL_EMPTY,
1264     29,
1265     NULL,
1266     NULL,
1267     NULL
1268   },
1269   {
1270     EL_EM_STEEL_EXIT_OPENING,
1271     EL_EM_STEEL_EXIT_OPEN,
1272     29,
1273     NULL,
1274     NULL,
1275     NULL
1276   },
1277   {
1278     EL_EM_STEEL_EXIT_CLOSING,
1279     EL_STEELWALL,
1280     29,
1281     NULL,
1282     NULL,
1283     NULL
1284   },
1285   {
1286     EL_SP_EXIT_OPENING,
1287     EL_SP_EXIT_OPEN,
1288     29,
1289     NULL,
1290     NULL,
1291     NULL
1292   },
1293   {
1294     EL_SP_EXIT_CLOSING,
1295     EL_SP_EXIT_CLOSED,
1296     29,
1297     NULL,
1298     NULL,
1299     NULL
1300   },
1301   {
1302     EL_SWITCHGATE_OPENING,
1303     EL_SWITCHGATE_OPEN,
1304     29,
1305     NULL,
1306     NULL,
1307     NULL
1308   },
1309   {
1310     EL_SWITCHGATE_CLOSING,
1311     EL_SWITCHGATE_CLOSED,
1312     29,
1313     NULL,
1314     NULL,
1315     NULL
1316   },
1317   {
1318     EL_TIMEGATE_OPENING,
1319     EL_TIMEGATE_OPEN,
1320     29,
1321     NULL,
1322     NULL,
1323     NULL
1324   },
1325   {
1326     EL_TIMEGATE_CLOSING,
1327     EL_TIMEGATE_CLOSED,
1328     29,
1329     NULL,
1330     NULL,
1331     NULL
1332   },
1333
1334   {
1335     EL_ACID_SPLASH_LEFT,
1336     EL_EMPTY,
1337     8,
1338     NULL,
1339     NULL,
1340     NULL
1341   },
1342   {
1343     EL_ACID_SPLASH_RIGHT,
1344     EL_EMPTY,
1345     8,
1346     NULL,
1347     NULL,
1348     NULL
1349   },
1350   {
1351     EL_SP_BUGGY_BASE,
1352     EL_SP_BUGGY_BASE_ACTIVATING,
1353     0,
1354     InitBuggyBase,
1355     NULL,
1356     NULL
1357   },
1358   {
1359     EL_SP_BUGGY_BASE_ACTIVATING,
1360     EL_SP_BUGGY_BASE_ACTIVE,
1361     0,
1362     InitBuggyBase,
1363     NULL,
1364     NULL
1365   },
1366   {
1367     EL_SP_BUGGY_BASE_ACTIVE,
1368     EL_SP_BUGGY_BASE,
1369     0,
1370     InitBuggyBase,
1371     WarnBuggyBase,
1372     NULL
1373   },
1374   {
1375     EL_TRAP,
1376     EL_TRAP_ACTIVE,
1377     0,
1378     InitTrap,
1379     NULL,
1380     ActivateTrap
1381   },
1382   {
1383     EL_TRAP_ACTIVE,
1384     EL_TRAP,
1385     31,
1386     NULL,
1387     ChangeActiveTrap,
1388     NULL
1389   },
1390   {
1391     EL_ROBOT_WHEEL_ACTIVE,
1392     EL_ROBOT_WHEEL,
1393     0,
1394     InitRobotWheel,
1395     RunRobotWheel,
1396     StopRobotWheel
1397   },
1398   {
1399     EL_TIMEGATE_SWITCH_ACTIVE,
1400     EL_TIMEGATE_SWITCH,
1401     0,
1402     InitTimegateWheel,
1403     RunTimegateWheel,
1404     NULL
1405   },
1406   {
1407     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1408     EL_DC_TIMEGATE_SWITCH,
1409     0,
1410     InitTimegateWheel,
1411     RunTimegateWheel,
1412     NULL
1413   },
1414   {
1415     EL_EMC_MAGIC_BALL_ACTIVE,
1416     EL_EMC_MAGIC_BALL_ACTIVE,
1417     0,
1418     InitMagicBallDelay,
1419     NULL,
1420     ActivateMagicBall
1421   },
1422   {
1423     EL_EMC_SPRING_BUMPER_ACTIVE,
1424     EL_EMC_SPRING_BUMPER,
1425     8,
1426     NULL,
1427     NULL,
1428     NULL
1429   },
1430   {
1431     EL_DIAGONAL_SHRINKING,
1432     EL_UNDEFINED,
1433     0,
1434     NULL,
1435     NULL,
1436     NULL
1437   },
1438   {
1439     EL_DIAGONAL_GROWING,
1440     EL_UNDEFINED,
1441     0,
1442     NULL,
1443     NULL,
1444     NULL,
1445   },
1446
1447   {
1448     EL_UNDEFINED,
1449     EL_UNDEFINED,
1450     -1,
1451     NULL,
1452     NULL,
1453     NULL
1454   }
1455 };
1456
1457 struct
1458 {
1459   int element;
1460   int push_delay_fixed, push_delay_random;
1461 }
1462 push_delay_list[] =
1463 {
1464   { EL_SPRING,                  0, 0 },
1465   { EL_BALLOON,                 0, 0 },
1466
1467   { EL_SOKOBAN_OBJECT,          2, 0 },
1468   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1469   { EL_SATELLITE,               2, 0 },
1470   { EL_SP_DISK_YELLOW,          2, 0 },
1471
1472   { EL_UNDEFINED,               0, 0 },
1473 };
1474
1475 struct
1476 {
1477   int element;
1478   int move_stepsize;
1479 }
1480 move_stepsize_list[] =
1481 {
1482   { EL_AMOEBA_DROP,             2 },
1483   { EL_AMOEBA_DROPPING,         2 },
1484   { EL_QUICKSAND_FILLING,       1 },
1485   { EL_QUICKSAND_EMPTYING,      1 },
1486   { EL_QUICKSAND_FAST_FILLING,  2 },
1487   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1488   { EL_MAGIC_WALL_FILLING,      2 },
1489   { EL_MAGIC_WALL_EMPTYING,     2 },
1490   { EL_BD_MAGIC_WALL_FILLING,   2 },
1491   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1492   { EL_DC_MAGIC_WALL_FILLING,   2 },
1493   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1494
1495   { EL_UNDEFINED,               0 },
1496 };
1497
1498 struct
1499 {
1500   int element;
1501   int count;
1502 }
1503 collect_count_list[] =
1504 {
1505   { EL_EMERALD,                 1 },
1506   { EL_BD_DIAMOND,              1 },
1507   { EL_EMERALD_YELLOW,          1 },
1508   { EL_EMERALD_RED,             1 },
1509   { EL_EMERALD_PURPLE,          1 },
1510   { EL_DIAMOND,                 3 },
1511   { EL_SP_INFOTRON,             1 },
1512   { EL_PEARL,                   5 },
1513   { EL_CRYSTAL,                 8 },
1514
1515   { EL_UNDEFINED,               0 },
1516 };
1517
1518 struct
1519 {
1520   int element;
1521   int direction;
1522 }
1523 access_direction_list[] =
1524 {
1525   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1526   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1527   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1528   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1529   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1530   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1531   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1532   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1533   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1534   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1535   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1536
1537   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1538   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1539   { EL_SP_PORT_UP,                                                   MV_DOWN },
1540   { EL_SP_PORT_DOWN,                                         MV_UP           },
1541   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1542   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1543   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1544   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1545   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1546   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1547   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1548   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1549   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1550   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1551   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1552   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1553   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1554   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1555   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1556
1557   { EL_UNDEFINED,                       MV_NONE                              }
1558 };
1559
1560 static struct XY xy_topdown[] =
1561 {
1562   {  0, -1 },
1563   { -1,  0 },
1564   { +1,  0 },
1565   {  0, +1 }
1566 };
1567
1568 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1569
1570 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1571 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1572 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Tile[x][y]) || \
1573                                  IS_JUST_CHANGING(x, y))
1574
1575 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1576
1577 // static variables for playfield scan mode (scanning forward or backward)
1578 static int playfield_scan_start_x = 0;
1579 static int playfield_scan_start_y = 0;
1580 static int playfield_scan_delta_x = 1;
1581 static int playfield_scan_delta_y = 1;
1582
1583 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1584                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1585                                      (y) += playfield_scan_delta_y)     \
1586                                 for ((x) = playfield_scan_start_x;      \
1587                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1588                                      (x) += playfield_scan_delta_x)
1589
1590 #ifdef DEBUG
1591 void DEBUG_SetMaximumDynamite(void)
1592 {
1593   int i;
1594
1595   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1596     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1597       local_player->inventory_element[local_player->inventory_size++] =
1598         EL_DYNAMITE;
1599 }
1600 #endif
1601
1602 static void InitPlayfieldScanModeVars(void)
1603 {
1604   if (game.use_reverse_scan_direction)
1605   {
1606     playfield_scan_start_x = lev_fieldx - 1;
1607     playfield_scan_start_y = lev_fieldy - 1;
1608
1609     playfield_scan_delta_x = -1;
1610     playfield_scan_delta_y = -1;
1611   }
1612   else
1613   {
1614     playfield_scan_start_x = 0;
1615     playfield_scan_start_y = 0;
1616
1617     playfield_scan_delta_x = 1;
1618     playfield_scan_delta_y = 1;
1619   }
1620 }
1621
1622 static void InitPlayfieldScanMode(int mode)
1623 {
1624   game.use_reverse_scan_direction =
1625     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1626
1627   InitPlayfieldScanModeVars();
1628 }
1629
1630 static int get_move_delay_from_stepsize(int move_stepsize)
1631 {
1632   move_stepsize =
1633     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1634
1635   // make sure that stepsize value is always a power of 2
1636   move_stepsize = (1 << log_2(move_stepsize));
1637
1638   return TILEX / move_stepsize;
1639 }
1640
1641 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1642                                boolean init_game)
1643 {
1644   int player_nr = player->index_nr;
1645   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1646   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1647
1648   // do no immediately change move delay -- the player might just be moving
1649   player->move_delay_value_next = move_delay;
1650
1651   // information if player can move must be set separately
1652   player->cannot_move = cannot_move;
1653
1654   if (init_game)
1655   {
1656     player->move_delay       = game.initial_move_delay[player_nr];
1657     player->move_delay_value = game.initial_move_delay_value[player_nr];
1658
1659     player->move_delay_value_next = -1;
1660
1661     player->move_delay_reset_counter = 0;
1662   }
1663 }
1664
1665 void GetPlayerConfig(void)
1666 {
1667   GameFrameDelay = setup.game_frame_delay;
1668
1669   if (!audio.sound_available)
1670     setup.sound_simple = FALSE;
1671
1672   if (!audio.loops_available)
1673     setup.sound_loops = FALSE;
1674
1675   if (!audio.music_available)
1676     setup.sound_music = FALSE;
1677
1678   if (!video.fullscreen_available)
1679     setup.fullscreen = FALSE;
1680
1681   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1682
1683   SetAudioMode(setup.sound);
1684 }
1685
1686 int GetElementFromGroupElement(int element)
1687 {
1688   if (IS_GROUP_ELEMENT(element))
1689   {
1690     struct ElementGroupInfo *group = element_info[element].group;
1691     int last_anim_random_frame = gfx.anim_random_frame;
1692     int element_pos;
1693
1694     if (group->choice_mode == ANIM_RANDOM)
1695       gfx.anim_random_frame = RND(group->num_elements_resolved);
1696
1697     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1698                                     group->choice_mode, 0,
1699                                     group->choice_pos);
1700
1701     if (group->choice_mode == ANIM_RANDOM)
1702       gfx.anim_random_frame = last_anim_random_frame;
1703
1704     group->choice_pos++;
1705
1706     element = group->element_resolved[element_pos];
1707   }
1708
1709   return element;
1710 }
1711
1712 static void IncrementSokobanFieldsNeeded(void)
1713 {
1714   if (level.sb_fields_needed)
1715     game.sokoban_fields_still_needed++;
1716 }
1717
1718 static void IncrementSokobanObjectsNeeded(void)
1719 {
1720   if (level.sb_objects_needed)
1721     game.sokoban_objects_still_needed++;
1722 }
1723
1724 static void DecrementSokobanFieldsNeeded(void)
1725 {
1726   if (game.sokoban_fields_still_needed > 0)
1727     game.sokoban_fields_still_needed--;
1728 }
1729
1730 static void DecrementSokobanObjectsNeeded(void)
1731 {
1732   if (game.sokoban_objects_still_needed > 0)
1733     game.sokoban_objects_still_needed--;
1734 }
1735
1736 static void InitPlayerField(int x, int y, int element, boolean init_game)
1737 {
1738   if (element == EL_SP_MURPHY)
1739   {
1740     if (init_game)
1741     {
1742       if (stored_player[0].present)
1743       {
1744         Tile[x][y] = EL_SP_MURPHY_CLONE;
1745
1746         return;
1747       }
1748       else
1749       {
1750         stored_player[0].initial_element = element;
1751         stored_player[0].use_murphy = TRUE;
1752
1753         if (!level.use_artwork_element[0])
1754           stored_player[0].artwork_element = EL_SP_MURPHY;
1755       }
1756
1757       Tile[x][y] = EL_PLAYER_1;
1758     }
1759   }
1760
1761   if (init_game)
1762   {
1763     struct PlayerInfo *player = &stored_player[Tile[x][y] - EL_PLAYER_1];
1764     int jx = player->jx, jy = player->jy;
1765
1766     player->present = TRUE;
1767
1768     player->block_last_field = (element == EL_SP_MURPHY ?
1769                                 level.sp_block_last_field :
1770                                 level.block_last_field);
1771
1772     // ---------- initialize player's last field block delay ------------------
1773
1774     // always start with reliable default value (no adjustment needed)
1775     player->block_delay_adjustment = 0;
1776
1777     // special case 1: in Supaplex, Murphy blocks last field one more frame
1778     if (player->block_last_field && element == EL_SP_MURPHY)
1779       player->block_delay_adjustment = 1;
1780
1781     // special case 2: in game engines before 3.1.1, blocking was different
1782     if (game.use_block_last_field_bug)
1783       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1784
1785     if (!network.enabled || player->connected_network)
1786     {
1787       player->active = TRUE;
1788
1789       // remove potentially duplicate players
1790       if (IN_LEV_FIELD(jx, jy) && StorePlayer[jx][jy] == Tile[x][y])
1791         StorePlayer[jx][jy] = 0;
1792
1793       StorePlayer[x][y] = Tile[x][y];
1794
1795 #if DEBUG_INIT_PLAYER
1796       Debug("game:init:player", "- player element %d activated",
1797             player->element_nr);
1798       Debug("game:init:player", "  (local player is %d and currently %s)",
1799             local_player->element_nr,
1800             local_player->active ? "active" : "not active");
1801     }
1802 #endif
1803
1804     Tile[x][y] = EL_EMPTY;
1805
1806     player->jx = player->last_jx = x;
1807     player->jy = player->last_jy = y;
1808   }
1809
1810   // always check if player was just killed and should be reanimated
1811   {
1812     int player_nr = GET_PLAYER_NR(element);
1813     struct PlayerInfo *player = &stored_player[player_nr];
1814
1815     if (player->active && player->killed)
1816       player->reanimated = TRUE; // if player was just killed, reanimate him
1817   }
1818 }
1819
1820 static void InitField(int x, int y, boolean init_game)
1821 {
1822   int element = Tile[x][y];
1823
1824   switch (element)
1825   {
1826     case EL_SP_MURPHY:
1827     case EL_PLAYER_1:
1828     case EL_PLAYER_2:
1829     case EL_PLAYER_3:
1830     case EL_PLAYER_4:
1831       InitPlayerField(x, y, element, init_game);
1832       break;
1833
1834     case EL_SOKOBAN_FIELD_PLAYER:
1835       element = Tile[x][y] = EL_PLAYER_1;
1836       InitField(x, y, init_game);
1837
1838       element = Tile[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1839       InitField(x, y, init_game);
1840       break;
1841
1842     case EL_SOKOBAN_FIELD_EMPTY:
1843       IncrementSokobanFieldsNeeded();
1844       break;
1845
1846     case EL_SOKOBAN_OBJECT:
1847       IncrementSokobanObjectsNeeded();
1848       break;
1849
1850     case EL_STONEBLOCK:
1851       if (x < lev_fieldx - 1 && Tile[x + 1][y] == EL_ACID)
1852         Tile[x][y] = EL_ACID_POOL_TOPLEFT;
1853       else if (x > 0 && Tile[x - 1][y] == EL_ACID)
1854         Tile[x][y] = EL_ACID_POOL_TOPRIGHT;
1855       else if (y > 0 && Tile[x][y - 1] == EL_ACID_POOL_TOPLEFT)
1856         Tile[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1857       else if (y > 0 && Tile[x][y - 1] == EL_ACID)
1858         Tile[x][y] = EL_ACID_POOL_BOTTOM;
1859       else if (y > 0 && Tile[x][y - 1] == EL_ACID_POOL_TOPRIGHT)
1860         Tile[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1861       break;
1862
1863     case EL_BUG:
1864     case EL_BUG_RIGHT:
1865     case EL_BUG_UP:
1866     case EL_BUG_LEFT:
1867     case EL_BUG_DOWN:
1868     case EL_SPACESHIP:
1869     case EL_SPACESHIP_RIGHT:
1870     case EL_SPACESHIP_UP:
1871     case EL_SPACESHIP_LEFT:
1872     case EL_SPACESHIP_DOWN:
1873     case EL_BD_BUTTERFLY:
1874     case EL_BD_BUTTERFLY_RIGHT:
1875     case EL_BD_BUTTERFLY_UP:
1876     case EL_BD_BUTTERFLY_LEFT:
1877     case EL_BD_BUTTERFLY_DOWN:
1878     case EL_BD_FIREFLY:
1879     case EL_BD_FIREFLY_RIGHT:
1880     case EL_BD_FIREFLY_UP:
1881     case EL_BD_FIREFLY_LEFT:
1882     case EL_BD_FIREFLY_DOWN:
1883     case EL_PACMAN_RIGHT:
1884     case EL_PACMAN_UP:
1885     case EL_PACMAN_LEFT:
1886     case EL_PACMAN_DOWN:
1887     case EL_YAMYAM:
1888     case EL_YAMYAM_LEFT:
1889     case EL_YAMYAM_RIGHT:
1890     case EL_YAMYAM_UP:
1891     case EL_YAMYAM_DOWN:
1892     case EL_DARK_YAMYAM:
1893     case EL_ROBOT:
1894     case EL_PACMAN:
1895     case EL_SP_SNIKSNAK:
1896     case EL_SP_ELECTRON:
1897     case EL_MOLE:
1898     case EL_MOLE_LEFT:
1899     case EL_MOLE_RIGHT:
1900     case EL_MOLE_UP:
1901     case EL_MOLE_DOWN:
1902     case EL_SPRING_LEFT:
1903     case EL_SPRING_RIGHT:
1904       InitMovDir(x, y);
1905       break;
1906
1907     case EL_AMOEBA_FULL:
1908     case EL_BD_AMOEBA:
1909       InitAmoebaNr(x, y);
1910       break;
1911
1912     case EL_AMOEBA_DROP:
1913       if (y == lev_fieldy - 1)
1914       {
1915         Tile[x][y] = EL_AMOEBA_GROWING;
1916         Store[x][y] = EL_AMOEBA_WET;
1917       }
1918       break;
1919
1920     case EL_DYNAMITE_ACTIVE:
1921     case EL_SP_DISK_RED_ACTIVE:
1922     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1923     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1924     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1925     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1926       MovDelay[x][y] = 96;
1927       break;
1928
1929     case EL_EM_DYNAMITE_ACTIVE:
1930       MovDelay[x][y] = 32;
1931       break;
1932
1933     case EL_LAMP:
1934       game.lights_still_needed++;
1935       break;
1936
1937     case EL_PENGUIN:
1938       game.friends_still_needed++;
1939       break;
1940
1941     case EL_PIG:
1942     case EL_DRAGON:
1943       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1944       break;
1945
1946     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1947     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1948     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1949     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1950     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1951     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1952     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1953     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1954     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1955     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1956     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1957     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1958       if (init_game)
1959       {
1960         int belt_nr = getBeltNrFromBeltSwitchElement(Tile[x][y]);
1961         int belt_dir = getBeltDirFromBeltSwitchElement(Tile[x][y]);
1962         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Tile[x][y]);
1963
1964         if (game.belt_dir_nr[belt_nr] == 3)     // initial value
1965         {
1966           game.belt_dir[belt_nr] = belt_dir;
1967           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1968         }
1969         else    // more than one switch -- set it like the first switch
1970         {
1971           Tile[x][y] = Tile[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1972         }
1973       }
1974       break;
1975
1976     case EL_LIGHT_SWITCH_ACTIVE:
1977       if (init_game)
1978         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1979       break;
1980
1981     case EL_INVISIBLE_STEELWALL:
1982     case EL_INVISIBLE_WALL:
1983     case EL_INVISIBLE_SAND:
1984       if (game.light_time_left > 0 ||
1985           game.lenses_time_left > 0)
1986         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
1987       break;
1988
1989     case EL_EMC_MAGIC_BALL:
1990       if (game.ball_active)
1991         Tile[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1992       break;
1993
1994     case EL_EMC_MAGIC_BALL_SWITCH:
1995       if (game.ball_active)
1996         Tile[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1997       break;
1998
1999     case EL_TRIGGER_PLAYER:
2000     case EL_TRIGGER_ELEMENT:
2001     case EL_TRIGGER_CE_VALUE:
2002     case EL_TRIGGER_CE_SCORE:
2003     case EL_SELF:
2004     case EL_ANY_ELEMENT:
2005     case EL_CURRENT_CE_VALUE:
2006     case EL_CURRENT_CE_SCORE:
2007     case EL_PREV_CE_1:
2008     case EL_PREV_CE_2:
2009     case EL_PREV_CE_3:
2010     case EL_PREV_CE_4:
2011     case EL_PREV_CE_5:
2012     case EL_PREV_CE_6:
2013     case EL_PREV_CE_7:
2014     case EL_PREV_CE_8:
2015     case EL_NEXT_CE_1:
2016     case EL_NEXT_CE_2:
2017     case EL_NEXT_CE_3:
2018     case EL_NEXT_CE_4:
2019     case EL_NEXT_CE_5:
2020     case EL_NEXT_CE_6:
2021     case EL_NEXT_CE_7:
2022     case EL_NEXT_CE_8:
2023       // reference elements should not be used on the playfield
2024       Tile[x][y] = EL_EMPTY;
2025       break;
2026
2027     default:
2028       if (IS_CUSTOM_ELEMENT(element))
2029       {
2030         if (CAN_MOVE(element))
2031           InitMovDir(x, y);
2032
2033         if (!element_info[element].use_last_ce_value || init_game)
2034           CustomValue[x][y] = GET_NEW_CE_VALUE(Tile[x][y]);
2035       }
2036       else if (IS_GROUP_ELEMENT(element))
2037       {
2038         Tile[x][y] = GetElementFromGroupElement(element);
2039
2040         InitField(x, y, init_game);
2041       }
2042       else if (IS_EMPTY_ELEMENT(element))
2043       {
2044         GfxElementEmpty[x][y] = element;
2045         Tile[x][y] = EL_EMPTY;
2046
2047         if (element_info[element].use_gfx_element)
2048           game.use_masked_elements = TRUE;
2049       }
2050
2051       break;
2052   }
2053
2054   if (!init_game)
2055     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2056 }
2057
2058 static void InitField_WithBug1(int x, int y, boolean init_game)
2059 {
2060   InitField(x, y, init_game);
2061
2062   // not needed to call InitMovDir() -- already done by InitField()!
2063   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2064       CAN_MOVE(Tile[x][y]))
2065     InitMovDir(x, y);
2066 }
2067
2068 static void InitField_WithBug2(int x, int y, boolean init_game)
2069 {
2070   int old_element = Tile[x][y];
2071
2072   InitField(x, y, init_game);
2073
2074   // not needed to call InitMovDir() -- already done by InitField()!
2075   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2076       CAN_MOVE(old_element) &&
2077       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2078     InitMovDir(x, y);
2079
2080   /* this case is in fact a combination of not less than three bugs:
2081      first, it calls InitMovDir() for elements that can move, although this is
2082      already done by InitField(); then, it checks the element that was at this
2083      field _before_ the call to InitField() (which can change it); lastly, it
2084      was not called for "mole with direction" elements, which were treated as
2085      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2086   */
2087 }
2088
2089 static int get_key_element_from_nr(int key_nr)
2090 {
2091   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2092                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2093                           EL_EM_KEY_1 : EL_KEY_1);
2094
2095   return key_base_element + key_nr;
2096 }
2097
2098 static int get_next_dropped_element(struct PlayerInfo *player)
2099 {
2100   return (player->inventory_size > 0 ?
2101           player->inventory_element[player->inventory_size - 1] :
2102           player->inventory_infinite_element != EL_UNDEFINED ?
2103           player->inventory_infinite_element :
2104           player->dynabombs_left > 0 ?
2105           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2106           EL_UNDEFINED);
2107 }
2108
2109 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2110 {
2111   // pos >= 0: get element from bottom of the stack;
2112   // pos <  0: get element from top of the stack
2113
2114   if (pos < 0)
2115   {
2116     int min_inventory_size = -pos;
2117     int inventory_pos = player->inventory_size - min_inventory_size;
2118     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2119
2120     return (player->inventory_size >= min_inventory_size ?
2121             player->inventory_element[inventory_pos] :
2122             player->inventory_infinite_element != EL_UNDEFINED ?
2123             player->inventory_infinite_element :
2124             player->dynabombs_left >= min_dynabombs_left ?
2125             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2126             EL_UNDEFINED);
2127   }
2128   else
2129   {
2130     int min_dynabombs_left = pos + 1;
2131     int min_inventory_size = pos + 1 - player->dynabombs_left;
2132     int inventory_pos = pos - player->dynabombs_left;
2133
2134     return (player->inventory_infinite_element != EL_UNDEFINED ?
2135             player->inventory_infinite_element :
2136             player->dynabombs_left >= min_dynabombs_left ?
2137             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2138             player->inventory_size >= min_inventory_size ?
2139             player->inventory_element[inventory_pos] :
2140             EL_UNDEFINED);
2141   }
2142 }
2143
2144 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2145 {
2146   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2147   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2148   int compare_result;
2149
2150   if (gpo1->sort_priority != gpo2->sort_priority)
2151     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2152   else
2153     compare_result = gpo1->nr - gpo2->nr;
2154
2155   return compare_result;
2156 }
2157
2158 int getPlayerInventorySize(int player_nr)
2159 {
2160   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2161     return game_em.ply[player_nr]->dynamite;
2162   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2163     return game_sp.red_disk_count;
2164   else
2165     return stored_player[player_nr].inventory_size;
2166 }
2167
2168 static void InitGameControlValues(void)
2169 {
2170   int i;
2171
2172   for (i = 0; game_panel_controls[i].nr != -1; i++)
2173   {
2174     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2175     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2176     struct TextPosInfo *pos = gpc->pos;
2177     int nr = gpc->nr;
2178     int type = gpc->type;
2179
2180     if (nr != i)
2181     {
2182       Error("'game_panel_controls' structure corrupted at %d", i);
2183
2184       Fail("this should not happen -- please debug");
2185     }
2186
2187     // force update of game controls after initialization
2188     gpc->value = gpc->last_value = -1;
2189     gpc->frame = gpc->last_frame = -1;
2190     gpc->gfx_frame = -1;
2191
2192     // determine panel value width for later calculation of alignment
2193     if (type == TYPE_INTEGER || type == TYPE_STRING)
2194     {
2195       pos->width = pos->size * getFontWidth(pos->font);
2196       pos->height = getFontHeight(pos->font);
2197     }
2198     else if (type == TYPE_ELEMENT)
2199     {
2200       pos->width = pos->size;
2201       pos->height = pos->size;
2202     }
2203
2204     // fill structure for game panel draw order
2205     gpo->nr = gpc->nr;
2206     gpo->sort_priority = pos->sort_priority;
2207   }
2208
2209   // sort game panel controls according to sort_priority and control number
2210   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2211         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2212 }
2213
2214 static void UpdatePlayfieldElementCount(void)
2215 {
2216   boolean use_element_count = FALSE;
2217   int i, j, x, y;
2218
2219   // first check if it is needed at all to calculate playfield element count
2220   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2221     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2222       use_element_count = TRUE;
2223
2224   if (!use_element_count)
2225     return;
2226
2227   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2228     element_info[i].element_count = 0;
2229
2230   SCAN_PLAYFIELD(x, y)
2231   {
2232     element_info[Tile[x][y]].element_count++;
2233   }
2234
2235   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2236     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2237       if (IS_IN_GROUP(j, i))
2238         element_info[EL_GROUP_START + i].element_count +=
2239           element_info[j].element_count;
2240 }
2241
2242 static void UpdateGameControlValues(void)
2243 {
2244   int i, k;
2245   int time = (game.LevelSolved ?
2246               game.LevelSolved_CountingTime :
2247               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2248               game_em.lev->time :
2249               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2250               game_sp.time_played :
2251               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2252               game_mm.energy_left :
2253               game.no_level_time_limit ? TimePlayed : TimeLeft);
2254   int score = (game.LevelSolved ?
2255                game.LevelSolved_CountingScore :
2256                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2257                game_em.lev->score :
2258                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2259                game_sp.score :
2260                level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2261                game_mm.score :
2262                game.score);
2263   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2264               game_em.lev->gems_needed :
2265               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2266               game_sp.infotrons_still_needed :
2267               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2268               game_mm.kettles_still_needed :
2269               game.gems_still_needed);
2270   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2271                      game_em.lev->gems_needed > 0 :
2272                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2273                      game_sp.infotrons_still_needed > 0 :
2274                      level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2275                      game_mm.kettles_still_needed > 0 ||
2276                      game_mm.lights_still_needed > 0 :
2277                      game.gems_still_needed > 0 ||
2278                      game.sokoban_fields_still_needed > 0 ||
2279                      game.sokoban_objects_still_needed > 0 ||
2280                      game.lights_still_needed > 0);
2281   int health = (game.LevelSolved ?
2282                 game.LevelSolved_CountingHealth :
2283                 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2284                 MM_HEALTH(game_mm.laser_overload_value) :
2285                 game.health);
2286   int sync_random_frame = INIT_GFX_RANDOM();    // random, but synchronized
2287
2288   UpdatePlayfieldElementCount();
2289
2290   // update game panel control values
2291
2292   // used instead of "level_nr" (for network games)
2293   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = levelset.level_nr;
2294   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2295
2296   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2297   for (i = 0; i < MAX_NUM_KEYS; i++)
2298     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2299   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2300   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2301
2302   if (game.centered_player_nr == -1)
2303   {
2304     for (i = 0; i < MAX_PLAYERS; i++)
2305     {
2306       // only one player in Supaplex game engine
2307       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2308         break;
2309
2310       for (k = 0; k < MAX_NUM_KEYS; k++)
2311       {
2312         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2313         {
2314           if (game_em.ply[i]->keys & (1 << k))
2315             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2316               get_key_element_from_nr(k);
2317         }
2318         else if (stored_player[i].key[k])
2319           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2320             get_key_element_from_nr(k);
2321       }
2322
2323       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2324         getPlayerInventorySize(i);
2325
2326       if (stored_player[i].num_white_keys > 0)
2327         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2328           EL_DC_KEY_WHITE;
2329
2330       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2331         stored_player[i].num_white_keys;
2332     }
2333   }
2334   else
2335   {
2336     int player_nr = game.centered_player_nr;
2337
2338     for (k = 0; k < MAX_NUM_KEYS; k++)
2339     {
2340       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2341       {
2342         if (game_em.ply[player_nr]->keys & (1 << k))
2343           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2344             get_key_element_from_nr(k);
2345       }
2346       else if (stored_player[player_nr].key[k])
2347         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2348           get_key_element_from_nr(k);
2349     }
2350
2351     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2352       getPlayerInventorySize(player_nr);
2353
2354     if (stored_player[player_nr].num_white_keys > 0)
2355       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2356
2357     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2358       stored_player[player_nr].num_white_keys;
2359   }
2360
2361   // re-arrange keys on game panel, if needed or if defined by style settings
2362   for (i = 0; i < MAX_NUM_KEYS + 1; i++)        // all normal keys + white key
2363   {
2364     int nr = GAME_PANEL_KEY_1 + i;
2365     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2366     struct TextPosInfo *pos = gpc->pos;
2367
2368     // skip check if key is not in the player's inventory
2369     if (gpc->value == EL_EMPTY)
2370       continue;
2371
2372     // check if keys should be arranged on panel from left to right
2373     if (pos->style == STYLE_LEFTMOST_POSITION)
2374     {
2375       // check previous key positions (left from current key)
2376       for (k = 0; k < i; k++)
2377       {
2378         int nr_new = GAME_PANEL_KEY_1 + k;
2379
2380         if (game_panel_controls[nr_new].value == EL_EMPTY)
2381         {
2382           game_panel_controls[nr_new].value = gpc->value;
2383           gpc->value = EL_EMPTY;
2384
2385           break;
2386         }
2387       }
2388     }
2389
2390     // check if "undefined" keys can be placed at some other position
2391     if (pos->x == -1 && pos->y == -1)
2392     {
2393       int nr_new = GAME_PANEL_KEY_1 + i % STD_NUM_KEYS;
2394
2395       // 1st try: display key at the same position as normal or EM keys
2396       if (game_panel_controls[nr_new].value == EL_EMPTY)
2397       {
2398         game_panel_controls[nr_new].value = gpc->value;
2399       }
2400       else
2401       {
2402         // 2nd try: display key at the next free position in the key panel
2403         for (k = 0; k < STD_NUM_KEYS; k++)
2404         {
2405           nr_new = GAME_PANEL_KEY_1 + k;
2406
2407           if (game_panel_controls[nr_new].value == EL_EMPTY)
2408           {
2409             game_panel_controls[nr_new].value = gpc->value;
2410
2411             break;
2412           }
2413         }
2414       }
2415     }
2416   }
2417
2418   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2419   {
2420     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2421       get_inventory_element_from_pos(local_player, i);
2422     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2423       get_inventory_element_from_pos(local_player, -i - 1);
2424   }
2425
2426   game_panel_controls[GAME_PANEL_SCORE].value = score;
2427   game_panel_controls[GAME_PANEL_HIGHSCORE].value = scores.entry[0].score;
2428
2429   game_panel_controls[GAME_PANEL_TIME].value = time;
2430
2431   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2432   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2433   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2434
2435   if (level.time == 0)
2436     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2437   else
2438     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2439
2440   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2441   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2442
2443   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2444
2445   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2446     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2447      EL_EMPTY);
2448   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2449     local_player->shield_normal_time_left;
2450   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2451     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2452      EL_EMPTY);
2453   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2454     local_player->shield_deadly_time_left;
2455
2456   game_panel_controls[GAME_PANEL_EXIT].value =
2457     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2458
2459   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2460     (game.ball_active ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2461   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2462     (game.ball_active ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2463      EL_EMC_MAGIC_BALL_SWITCH);
2464
2465   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2466     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2467   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2468     game.light_time_left;
2469
2470   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2471     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2472   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2473     game.timegate_time_left;
2474
2475   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2476     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2477
2478   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2479     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2480   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2481     game.lenses_time_left;
2482
2483   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2484     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2485   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2486     game.magnify_time_left;
2487
2488   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2489     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2490      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2491      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2492      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2493      EL_BALLOON_SWITCH_NONE);
2494
2495   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2496     local_player->dynabomb_count;
2497   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2498     local_player->dynabomb_size;
2499   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2500     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2501
2502   game_panel_controls[GAME_PANEL_PENGUINS].value =
2503     game.friends_still_needed;
2504
2505   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2506     game.sokoban_objects_still_needed;
2507   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2508     game.sokoban_fields_still_needed;
2509
2510   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2511     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2512
2513   for (i = 0; i < NUM_BELTS; i++)
2514   {
2515     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2516       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2517        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2518     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2519       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2520   }
2521
2522   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2523     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2524   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2525     game.magic_wall_time_left;
2526
2527   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2528     local_player->gravity;
2529
2530   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2531     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2532
2533   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2534     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2535       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2536        game.panel.element[i].id : EL_UNDEFINED);
2537
2538   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2539     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2540       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2541        element_info[game.panel.element_count[i].id].element_count : 0);
2542
2543   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2544     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2545       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2546        element_info[game.panel.ce_score[i].id].collect_score : 0);
2547
2548   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2549     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2550       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2551        element_info[game.panel.ce_score_element[i].id].collect_score :
2552        EL_UNDEFINED);
2553
2554   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2555   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2556   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2557
2558   // update game panel control frames
2559
2560   for (i = 0; game_panel_controls[i].nr != -1; i++)
2561   {
2562     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2563
2564     if (gpc->type == TYPE_ELEMENT)
2565     {
2566       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2567       {
2568         int last_anim_random_frame = gfx.anim_random_frame;
2569         int element = gpc->value;
2570         int graphic = el2panelimg(element);
2571         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2572                                sync_random_frame :
2573                                graphic_info[graphic].anim_global_anim_sync ?
2574                                getGlobalAnimSyncFrame() : INIT_GFX_RANDOM());
2575
2576         if (gpc->value != gpc->last_value)
2577         {
2578           gpc->gfx_frame = 0;
2579           gpc->gfx_random = init_gfx_random;
2580         }
2581         else
2582         {
2583           gpc->gfx_frame++;
2584
2585           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2586               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2587             gpc->gfx_random = init_gfx_random;
2588         }
2589
2590         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2591           gfx.anim_random_frame = gpc->gfx_random;
2592
2593         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2594           gpc->gfx_frame = element_info[element].collect_score;
2595
2596         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2597
2598         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2599           gfx.anim_random_frame = last_anim_random_frame;
2600       }
2601     }
2602     else if (gpc->type == TYPE_GRAPHIC)
2603     {
2604       if (gpc->graphic != IMG_UNDEFINED)
2605       {
2606         int last_anim_random_frame = gfx.anim_random_frame;
2607         int graphic = gpc->graphic;
2608         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2609                                sync_random_frame :
2610                                graphic_info[graphic].anim_global_anim_sync ?
2611                                getGlobalAnimSyncFrame() : INIT_GFX_RANDOM());
2612
2613         if (gpc->value != gpc->last_value)
2614         {
2615           gpc->gfx_frame = 0;
2616           gpc->gfx_random = init_gfx_random;
2617         }
2618         else
2619         {
2620           gpc->gfx_frame++;
2621
2622           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2623               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2624             gpc->gfx_random = init_gfx_random;
2625         }
2626
2627         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2628           gfx.anim_random_frame = gpc->gfx_random;
2629
2630         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2631
2632         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2633           gfx.anim_random_frame = last_anim_random_frame;
2634       }
2635     }
2636   }
2637 }
2638
2639 static void DisplayGameControlValues(void)
2640 {
2641   boolean redraw_panel = FALSE;
2642   int i;
2643
2644   for (i = 0; game_panel_controls[i].nr != -1; i++)
2645   {
2646     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2647
2648     if (PANEL_DEACTIVATED(gpc->pos))
2649       continue;
2650
2651     if (gpc->value == gpc->last_value &&
2652         gpc->frame == gpc->last_frame)
2653       continue;
2654
2655     redraw_panel = TRUE;
2656   }
2657
2658   if (!redraw_panel)
2659     return;
2660
2661   // copy default game door content to main double buffer
2662
2663   // !!! CHECK AGAIN !!!
2664   SetPanelBackground();
2665   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2666   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2667
2668   // redraw game control buttons
2669   RedrawGameButtons();
2670
2671   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2672
2673   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2674   {
2675     int nr = game_panel_order[i].nr;
2676     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2677     struct TextPosInfo *pos = gpc->pos;
2678     int type = gpc->type;
2679     int value = gpc->value;
2680     int frame = gpc->frame;
2681     int size = pos->size;
2682     int font = pos->font;
2683     boolean draw_masked = pos->draw_masked;
2684     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2685
2686     if (PANEL_DEACTIVATED(pos))
2687       continue;
2688
2689     if (pos->class == get_hash_from_key("extra_panel_items") &&
2690         !setup.prefer_extra_panel_items)
2691       continue;
2692
2693     gpc->last_value = value;
2694     gpc->last_frame = frame;
2695
2696     if (type == TYPE_INTEGER)
2697     {
2698       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2699           nr == GAME_PANEL_INVENTORY_COUNT ||
2700           nr == GAME_PANEL_SCORE ||
2701           nr == GAME_PANEL_HIGHSCORE ||
2702           nr == GAME_PANEL_TIME)
2703       {
2704         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2705
2706         if (use_dynamic_size)           // use dynamic number of digits
2707         {
2708           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 :
2709                               nr == GAME_PANEL_INVENTORY_COUNT ||
2710                               nr == GAME_PANEL_TIME ? 1000 : 100000);
2711           int size_add = (nr == GAME_PANEL_LEVEL_NUMBER ||
2712                           nr == GAME_PANEL_INVENTORY_COUNT ||
2713                           nr == GAME_PANEL_TIME ? 1 : 2);
2714           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 :
2715                        nr == GAME_PANEL_INVENTORY_COUNT ||
2716                        nr == GAME_PANEL_TIME ? 3 : 5);
2717           int size2 = size1 + size_add;
2718           int font1 = pos->font;
2719           int font2 = pos->font_alt;
2720
2721           size = (value < value_change ? size1 : size2);
2722           font = (value < value_change ? font1 : font2);
2723         }
2724       }
2725
2726       // correct text size if "digits" is zero or less
2727       if (size <= 0)
2728         size = strlen(int2str(value, size));
2729
2730       // dynamically correct text alignment
2731       pos->width = size * getFontWidth(font);
2732
2733       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2734                   int2str(value, size), font, mask_mode);
2735     }
2736     else if (type == TYPE_ELEMENT)
2737     {
2738       int element, graphic;
2739       Bitmap *src_bitmap;
2740       int src_x, src_y;
2741       int width, height;
2742       int dst_x = PANEL_XPOS(pos);
2743       int dst_y = PANEL_YPOS(pos);
2744
2745       if (value != EL_UNDEFINED && value != EL_EMPTY)
2746       {
2747         element = value;
2748         graphic = el2panelimg(value);
2749
2750 #if 0
2751         Debug("game:DisplayGameControlValues", "%d, '%s' [%d]",
2752               element, EL_NAME(element), size);
2753 #endif
2754
2755         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2756           size = TILESIZE;
2757
2758         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2759                               &src_x, &src_y);
2760
2761         width  = graphic_info[graphic].width  * size / TILESIZE;
2762         height = graphic_info[graphic].height * size / TILESIZE;
2763
2764         if (draw_masked)
2765           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2766                            dst_x, dst_y);
2767         else
2768           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2769                      dst_x, dst_y);
2770       }
2771     }
2772     else if (type == TYPE_GRAPHIC)
2773     {
2774       int graphic        = gpc->graphic;
2775       int graphic_active = gpc->graphic_active;
2776       Bitmap *src_bitmap;
2777       int src_x, src_y;
2778       int width, height;
2779       int dst_x = PANEL_XPOS(pos);
2780       int dst_y = PANEL_YPOS(pos);
2781       boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2782                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2783
2784       if (graphic != IMG_UNDEFINED && !skip)
2785       {
2786         if (pos->style == STYLE_REVERSE)
2787           value = 100 - value;
2788
2789         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2790
2791         if (pos->direction & MV_HORIZONTAL)
2792         {
2793           width  = graphic_info[graphic_active].width * value / 100;
2794           height = graphic_info[graphic_active].height;
2795
2796           if (pos->direction == MV_LEFT)
2797           {
2798             src_x += graphic_info[graphic_active].width - width;
2799             dst_x += graphic_info[graphic_active].width - width;
2800           }
2801         }
2802         else
2803         {
2804           width  = graphic_info[graphic_active].width;
2805           height = graphic_info[graphic_active].height * value / 100;
2806
2807           if (pos->direction == MV_UP)
2808           {
2809             src_y += graphic_info[graphic_active].height - height;
2810             dst_y += graphic_info[graphic_active].height - height;
2811           }
2812         }
2813
2814         if (draw_masked)
2815           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2816                            dst_x, dst_y);
2817         else
2818           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2819                      dst_x, dst_y);
2820
2821         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2822
2823         if (pos->direction & MV_HORIZONTAL)
2824         {
2825           if (pos->direction == MV_RIGHT)
2826           {
2827             src_x += width;
2828             dst_x += width;
2829           }
2830           else
2831           {
2832             dst_x = PANEL_XPOS(pos);
2833           }
2834
2835           width = graphic_info[graphic].width - width;
2836         }
2837         else
2838         {
2839           if (pos->direction == MV_DOWN)
2840           {
2841             src_y += height;
2842             dst_y += height;
2843           }
2844           else
2845           {
2846             dst_y = PANEL_YPOS(pos);
2847           }
2848
2849           height = graphic_info[graphic].height - height;
2850         }
2851
2852         if (draw_masked)
2853           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2854                            dst_x, dst_y);
2855         else
2856           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2857                      dst_x, dst_y);
2858       }
2859     }
2860     else if (type == TYPE_STRING)
2861     {
2862       boolean active = (value != 0);
2863       char *state_normal = "off";
2864       char *state_active = "on";
2865       char *state = (active ? state_active : state_normal);
2866       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2867                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2868                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2869                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2870
2871       if (nr == GAME_PANEL_GRAVITY_STATE)
2872       {
2873         int font1 = pos->font;          // (used for normal state)
2874         int font2 = pos->font_alt;      // (used for active state)
2875
2876         font = (active ? font2 : font1);
2877       }
2878
2879       if (s != NULL)
2880       {
2881         char *s_cut;
2882
2883         if (size <= 0)
2884         {
2885           // don't truncate output if "chars" is zero or less
2886           size = strlen(s);
2887
2888           // dynamically correct text alignment
2889           pos->width = size * getFontWidth(font);
2890         }
2891
2892         s_cut = getStringCopyN(s, size);
2893
2894         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2895                     s_cut, font, mask_mode);
2896
2897         free(s_cut);
2898       }
2899     }
2900
2901     redraw_mask |= REDRAW_DOOR_1;
2902   }
2903
2904   SetGameStatus(GAME_MODE_PLAYING);
2905 }
2906
2907 void UpdateAndDisplayGameControlValues(void)
2908 {
2909   if (tape.deactivate_display)
2910     return;
2911
2912   UpdateGameControlValues();
2913   DisplayGameControlValues();
2914 }
2915
2916 void UpdateGameDoorValues(void)
2917 {
2918   UpdateGameControlValues();
2919 }
2920
2921 void DrawGameDoorValues(void)
2922 {
2923   DisplayGameControlValues();
2924 }
2925
2926
2927 // ============================================================================
2928 // InitGameEngine()
2929 // ----------------------------------------------------------------------------
2930 // initialize game engine due to level / tape version number
2931 // ============================================================================
2932
2933 static void InitGameEngine(void)
2934 {
2935   int i, j, k, l, x, y;
2936
2937   // set game engine from tape file when re-playing, else from level file
2938   game.engine_version = (tape.playing ? tape.engine_version :
2939                          level.game_version);
2940
2941   // set single or multi-player game mode (needed for re-playing tapes)
2942   game.team_mode = setup.team_mode;
2943
2944   if (tape.playing)
2945   {
2946     int num_players = 0;
2947
2948     for (i = 0; i < MAX_PLAYERS; i++)
2949       if (tape.player_participates[i])
2950         num_players++;
2951
2952     // multi-player tapes contain input data for more than one player
2953     game.team_mode = (num_players > 1);
2954   }
2955
2956 #if 0
2957   Debug("game:init:level", "level %d: level.game_version  == %06d", level_nr,
2958         level.game_version);
2959   Debug("game:init:level", "          tape.file_version   == %06d",
2960         tape.file_version);
2961   Debug("game:init:level", "          tape.game_version   == %06d",
2962         tape.game_version);
2963   Debug("game:init:level", "          tape.engine_version == %06d",
2964         tape.engine_version);
2965   Debug("game:init:level", "       => game.engine_version == %06d [tape mode: %s]",
2966         game.engine_version, (tape.playing ? "PLAYING" : "RECORDING"));
2967 #endif
2968
2969   // --------------------------------------------------------------------------
2970   // set flags for bugs and changes according to active game engine version
2971   // --------------------------------------------------------------------------
2972
2973   /*
2974     Summary of bugfix:
2975     Fixed property "can fall" for run-time element "EL_AMOEBA_DROPPING"
2976
2977     Bug was introduced in version:
2978     2.0.1
2979
2980     Bug was fixed in version:
2981     4.2.0.0
2982
2983     Description:
2984     In version 2.0.1, a new run-time element "EL_AMOEBA_DROPPING" was added,
2985     but the property "can fall" was missing, which caused some levels to be
2986     unsolvable. This was fixed in version 4.2.0.0.
2987
2988     Affected levels/tapes:
2989     An example for a tape that was fixed by this bugfix is tape 029 from the
2990     level set "rnd_sam_bateman".
2991     The wrong behaviour will still be used for all levels or tapes that were
2992     created/recorded with it. An example for this is tape 023 from the level
2993     set "rnd_gerhard_haeusler", which was recorded with a buggy game engine.
2994   */
2995
2996   boolean use_amoeba_dropping_cannot_fall_bug =
2997     ((game.engine_version >= VERSION_IDENT(2,0,1,0) &&
2998       game.engine_version <  VERSION_IDENT(4,2,0,0)) ||
2999      (tape.playing &&
3000       tape.game_version >= VERSION_IDENT(2,0,1,0) &&
3001       tape.game_version <  VERSION_IDENT(4,2,0,0)));
3002
3003   /*
3004     Summary of bugfix/change:
3005     Fixed move speed of elements entering or leaving magic wall.
3006
3007     Fixed/changed in version:
3008     2.0.1
3009
3010     Description:
3011     Before 2.0.1, move speed of elements entering or leaving magic wall was
3012     twice as fast as it is now.
3013     Since 2.0.1, this is set to a lower value by using move_stepsize_list[].
3014
3015     Affected levels/tapes:
3016     The first condition is generally needed for all levels/tapes before version
3017     2.0.1, which might use the old behaviour before it was changed; known tapes
3018     that are affected: Tape 014 from the level set "rnd_conor_mancone".
3019     The second condition is an exception from the above case and is needed for
3020     the special case of tapes recorded with game (not engine!) version 2.0.1 or
3021     above, but before it was known that this change would break tapes like the
3022     above and was fixed in 4.2.0.0, so that the changed behaviour was active
3023     although the engine version while recording maybe was before 2.0.1. There
3024     are a lot of tapes that are affected by this exception, like tape 006 from
3025     the level set "rnd_conor_mancone".
3026   */
3027
3028   boolean use_old_move_stepsize_for_magic_wall =
3029     (game.engine_version < VERSION_IDENT(2,0,1,0) &&
3030      !(tape.playing &&
3031        tape.game_version >= VERSION_IDENT(2,0,1,0) &&
3032        tape.game_version <  VERSION_IDENT(4,2,0,0)));
3033
3034   /*
3035     Summary of bugfix/change:
3036     Fixed handling for custom elements that change when pushed by the player.
3037
3038     Fixed/changed in version:
3039     3.1.0
3040
3041     Description:
3042     Before 3.1.0, custom elements that "change when pushing" changed directly
3043     after the player started pushing them (until then handled in "DigField()").
3044     Since 3.1.0, these custom elements are not changed until the "pushing"
3045     move of the element is finished (now handled in "ContinueMoving()").
3046
3047     Affected levels/tapes:
3048     The first condition is generally needed for all levels/tapes before version
3049     3.1.0, which might use the old behaviour before it was changed; known tapes
3050     that are affected are some tapes from the level set "Walpurgis Gardens" by
3051     Jamie Cullen.
3052     The second condition is an exception from the above case and is needed for
3053     the special case of tapes recorded with game (not engine!) version 3.1.0 or
3054     above (including some development versions of 3.1.0), but before it was
3055     known that this change would break tapes like the above and was fixed in
3056     3.1.1, so that the changed behaviour was active although the engine version
3057     while recording maybe was before 3.1.0. There is at least one tape that is
3058     affected by this exception, which is the tape for the one-level set "Bug
3059     Machine" by Juergen Bonhagen.
3060   */
3061
3062   game.use_change_when_pushing_bug =
3063     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3064      !(tape.playing &&
3065        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3066        tape.game_version <  VERSION_IDENT(3,1,1,0)));
3067
3068   /*
3069     Summary of bugfix/change:
3070     Fixed handling for blocking the field the player leaves when moving.
3071
3072     Fixed/changed in version:
3073     3.1.1
3074
3075     Description:
3076     Before 3.1.1, when "block last field when moving" was enabled, the field
3077     the player is leaving when moving was blocked for the time of the move,
3078     and was directly unblocked afterwards. This resulted in the last field
3079     being blocked for exactly one less than the number of frames of one player
3080     move. Additionally, even when blocking was disabled, the last field was
3081     blocked for exactly one frame.
3082     Since 3.1.1, due to changes in player movement handling, the last field
3083     is not blocked at all when blocking is disabled. When blocking is enabled,
3084     the last field is blocked for exactly the number of frames of one player
3085     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3086     last field is blocked for exactly one more than the number of frames of
3087     one player move.
3088
3089     Affected levels/tapes:
3090     (!!! yet to be determined -- probably many !!!)
3091   */
3092
3093   game.use_block_last_field_bug =
3094     (game.engine_version < VERSION_IDENT(3,1,1,0));
3095
3096   /* various special flags and settings for native Emerald Mine game engine */
3097
3098   game_em.use_single_button =
3099     (game.engine_version > VERSION_IDENT(4,0,0,2));
3100
3101   game_em.use_push_delay =
3102     (game.engine_version > VERSION_IDENT(4,3,7,1));
3103
3104   game_em.use_snap_key_bug =
3105     (game.engine_version < VERSION_IDENT(4,0,1,0));
3106
3107   game_em.use_random_bug =
3108     (tape.property_bits & TAPE_PROPERTY_EM_RANDOM_BUG);
3109
3110   boolean use_old_em_engine = (game.engine_version < VERSION_IDENT(4,2,0,0));
3111
3112   game_em.use_old_explosions            = use_old_em_engine;
3113   game_em.use_old_android               = use_old_em_engine;
3114   game_em.use_old_push_elements         = use_old_em_engine;
3115   game_em.use_old_push_into_acid        = use_old_em_engine;
3116
3117   game_em.use_wrap_around               = !use_old_em_engine;
3118
3119   // --------------------------------------------------------------------------
3120
3121   // set maximal allowed number of custom element changes per game frame
3122   game.max_num_changes_per_frame = 1;
3123
3124   // default scan direction: scan playfield from top/left to bottom/right
3125   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3126
3127   // dynamically adjust element properties according to game engine version
3128   InitElementPropertiesEngine(game.engine_version);
3129
3130   // ---------- initialize special element properties -------------------------
3131
3132   // "EL_AMOEBA_DROPPING" missed property "can fall" in older game versions
3133   if (use_amoeba_dropping_cannot_fall_bug)
3134     SET_PROPERTY(EL_AMOEBA_DROPPING, EP_CAN_FALL, FALSE);
3135
3136   // ---------- initialize player's initial move delay ------------------------
3137
3138   // dynamically adjust player properties according to level information
3139   for (i = 0; i < MAX_PLAYERS; i++)
3140     game.initial_move_delay_value[i] =
3141       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3142
3143   // dynamically adjust player properties according to game engine version
3144   for (i = 0; i < MAX_PLAYERS; i++)
3145     game.initial_move_delay[i] =
3146       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3147        game.initial_move_delay_value[i] : 0);
3148
3149   // ---------- initialize player's initial push delay ------------------------
3150
3151   // dynamically adjust player properties according to game engine version
3152   game.initial_push_delay_value =
3153     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3154
3155   // ---------- initialize changing elements ----------------------------------
3156
3157   // initialize changing elements information
3158   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3159   {
3160     struct ElementInfo *ei = &element_info[i];
3161
3162     // this pointer might have been changed in the level editor
3163     ei->change = &ei->change_page[0];
3164
3165     if (!IS_CUSTOM_ELEMENT(i))
3166     {
3167       ei->change->target_element = EL_EMPTY_SPACE;
3168       ei->change->delay_fixed = 0;
3169       ei->change->delay_random = 0;
3170       ei->change->delay_frames = 1;
3171     }
3172
3173     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3174     {
3175       ei->has_change_event[j] = FALSE;
3176
3177       ei->event_page_nr[j] = 0;
3178       ei->event_page[j] = &ei->change_page[0];
3179     }
3180   }
3181
3182   // add changing elements from pre-defined list
3183   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3184   {
3185     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3186     struct ElementInfo *ei = &element_info[ch_delay->element];
3187
3188     ei->change->target_element       = ch_delay->target_element;
3189     ei->change->delay_fixed          = ch_delay->change_delay;
3190
3191     ei->change->pre_change_function  = ch_delay->pre_change_function;
3192     ei->change->change_function      = ch_delay->change_function;
3193     ei->change->post_change_function = ch_delay->post_change_function;
3194
3195     ei->change->can_change = TRUE;
3196     ei->change->can_change_or_has_action = TRUE;
3197
3198     ei->has_change_event[CE_DELAY] = TRUE;
3199
3200     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3201     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3202   }
3203
3204   // ---------- initialize if element can trigger global animations -----------
3205
3206   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3207   {
3208     struct ElementInfo *ei = &element_info[i];
3209
3210     ei->has_anim_event = FALSE;
3211   }
3212
3213   InitGlobalAnimEventsForCustomElements();
3214
3215   // ---------- initialize internal run-time variables ------------------------
3216
3217   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3218   {
3219     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3220
3221     for (j = 0; j < ei->num_change_pages; j++)
3222     {
3223       ei->change_page[j].can_change_or_has_action =
3224         (ei->change_page[j].can_change |
3225          ei->change_page[j].has_action);
3226     }
3227   }
3228
3229   // add change events from custom element configuration
3230   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3231   {
3232     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3233
3234     for (j = 0; j < ei->num_change_pages; j++)
3235     {
3236       if (!ei->change_page[j].can_change_or_has_action)
3237         continue;
3238
3239       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3240       {
3241         // only add event page for the first page found with this event
3242         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3243         {
3244           ei->has_change_event[k] = TRUE;
3245
3246           ei->event_page_nr[k] = j;
3247           ei->event_page[k] = &ei->change_page[j];
3248         }
3249       }
3250     }
3251   }
3252
3253   // ---------- initialize reference elements in change conditions ------------
3254
3255   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3256   {
3257     int element = EL_CUSTOM_START + i;
3258     struct ElementInfo *ei = &element_info[element];
3259
3260     for (j = 0; j < ei->num_change_pages; j++)
3261     {
3262       int trigger_element = ei->change_page[j].initial_trigger_element;
3263
3264       if (trigger_element >= EL_PREV_CE_8 &&
3265           trigger_element <= EL_NEXT_CE_8)
3266         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3267
3268       ei->change_page[j].trigger_element = trigger_element;
3269     }
3270   }
3271
3272   // ---------- initialize run-time trigger player and element ----------------
3273
3274   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3275   {
3276     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3277
3278     for (j = 0; j < ei->num_change_pages; j++)
3279     {
3280       struct ElementChangeInfo *change = &ei->change_page[j];
3281
3282       change->actual_trigger_element = EL_EMPTY;
3283       change->actual_trigger_player = EL_EMPTY;
3284       change->actual_trigger_player_bits = CH_PLAYER_NONE;
3285       change->actual_trigger_side = CH_SIDE_NONE;
3286       change->actual_trigger_ce_value = 0;
3287       change->actual_trigger_ce_score = 0;
3288       change->actual_trigger_x = -1;
3289       change->actual_trigger_y = -1;
3290     }
3291   }
3292
3293   // ---------- initialize trigger events -------------------------------------
3294
3295   // initialize trigger events information
3296   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3297     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3298       trigger_events[i][j] = FALSE;
3299
3300   // add trigger events from element change event properties
3301   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3302   {
3303     struct ElementInfo *ei = &element_info[i];
3304
3305     for (j = 0; j < ei->num_change_pages; j++)
3306     {
3307       struct ElementChangeInfo *change = &ei->change_page[j];
3308
3309       if (!change->can_change_or_has_action)
3310         continue;
3311
3312       if (change->has_event[CE_BY_OTHER_ACTION])
3313       {
3314         int trigger_element = change->trigger_element;
3315
3316         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3317         {
3318           if (change->has_event[k])
3319           {
3320             if (IS_GROUP_ELEMENT(trigger_element))
3321             {
3322               struct ElementGroupInfo *group =
3323                 element_info[trigger_element].group;
3324
3325               for (l = 0; l < group->num_elements_resolved; l++)
3326                 trigger_events[group->element_resolved[l]][k] = TRUE;
3327             }
3328             else if (trigger_element == EL_ANY_ELEMENT)
3329               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3330                 trigger_events[l][k] = TRUE;
3331             else
3332               trigger_events[trigger_element][k] = TRUE;
3333           }
3334         }
3335       }
3336     }
3337   }
3338
3339   // ---------- initialize push delay -----------------------------------------
3340
3341   // initialize push delay values to default
3342   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3343   {
3344     if (!IS_CUSTOM_ELEMENT(i))
3345     {
3346       // set default push delay values (corrected since version 3.0.7-1)
3347       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3348       {
3349         element_info[i].push_delay_fixed = 2;
3350         element_info[i].push_delay_random = 8;
3351       }
3352       else
3353       {
3354         element_info[i].push_delay_fixed = 8;
3355         element_info[i].push_delay_random = 8;
3356       }
3357     }
3358   }
3359
3360   // set push delay value for certain elements from pre-defined list
3361   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3362   {
3363     int e = push_delay_list[i].element;
3364
3365     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3366     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3367   }
3368
3369   // set push delay value for Supaplex elements for newer engine versions
3370   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3371   {
3372     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3373     {
3374       if (IS_SP_ELEMENT(i))
3375       {
3376         // set SP push delay to just enough to push under a falling zonk
3377         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3378
3379         element_info[i].push_delay_fixed  = delay;
3380         element_info[i].push_delay_random = 0;
3381       }
3382     }
3383   }
3384
3385   // ---------- initialize move stepsize --------------------------------------
3386
3387   // initialize move stepsize values to default
3388   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3389     if (!IS_CUSTOM_ELEMENT(i))
3390       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3391
3392   // set move stepsize value for certain elements from pre-defined list
3393   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3394   {
3395     int e = move_stepsize_list[i].element;
3396
3397     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3398
3399     // set move stepsize value for certain elements for older engine versions
3400     if (use_old_move_stepsize_for_magic_wall)
3401     {
3402       if (e == EL_MAGIC_WALL_FILLING ||
3403           e == EL_MAGIC_WALL_EMPTYING ||
3404           e == EL_BD_MAGIC_WALL_FILLING ||
3405           e == EL_BD_MAGIC_WALL_EMPTYING)
3406         element_info[e].move_stepsize *= 2;
3407     }
3408   }
3409
3410   // ---------- initialize collect score --------------------------------------
3411
3412   // initialize collect score values for custom elements from initial value
3413   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3414     if (IS_CUSTOM_ELEMENT(i))
3415       element_info[i].collect_score = element_info[i].collect_score_initial;
3416
3417   // ---------- initialize collect count --------------------------------------
3418
3419   // initialize collect count values for non-custom elements
3420   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3421     if (!IS_CUSTOM_ELEMENT(i))
3422       element_info[i].collect_count_initial = 0;
3423
3424   // add collect count values for all elements from pre-defined list
3425   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3426     element_info[collect_count_list[i].element].collect_count_initial =
3427       collect_count_list[i].count;
3428
3429   // ---------- initialize access direction -----------------------------------
3430
3431   // initialize access direction values to default (access from every side)
3432   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3433     if (!IS_CUSTOM_ELEMENT(i))
3434       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3435
3436   // set access direction value for certain elements from pre-defined list
3437   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3438     element_info[access_direction_list[i].element].access_direction =
3439       access_direction_list[i].direction;
3440
3441   // ---------- initialize explosion content ----------------------------------
3442   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3443   {
3444     if (IS_CUSTOM_ELEMENT(i))
3445       continue;
3446
3447     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3448     {
3449       // (content for EL_YAMYAM set at run-time with game.yamyam_content_nr)
3450
3451       element_info[i].content.e[x][y] =
3452         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3453          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3454          i == EL_PLAYER_3 ? EL_EMERALD :
3455          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3456          i == EL_MOLE ? EL_EMERALD_RED :
3457          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3458          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3459          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3460          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3461          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3462          i == EL_WALL_EMERALD ? EL_EMERALD :
3463          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3464          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3465          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3466          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3467          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3468          i == EL_WALL_PEARL ? EL_PEARL :
3469          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3470          EL_EMPTY);
3471     }
3472   }
3473
3474   // ---------- initialize recursion detection --------------------------------
3475   recursion_loop_depth = 0;
3476   recursion_loop_detected = FALSE;
3477   recursion_loop_element = EL_UNDEFINED;
3478
3479   // ---------- initialize graphics engine ------------------------------------
3480   game.scroll_delay_value =
3481     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3482      level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3483      !setup.forced_scroll_delay           ? 0 :
3484      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3485   if (game.forced_scroll_delay_value == -1)
3486     game.scroll_delay_value =
3487       MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3488
3489   // ---------- initialize game engine snapshots ------------------------------
3490   for (i = 0; i < MAX_PLAYERS; i++)
3491     game.snapshot.last_action[i] = 0;
3492   game.snapshot.changed_action = FALSE;
3493   game.snapshot.collected_item = FALSE;
3494   game.snapshot.mode =
3495     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3496      SNAPSHOT_MODE_EVERY_STEP :
3497      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3498      SNAPSHOT_MODE_EVERY_MOVE :
3499      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3500      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3501   game.snapshot.save_snapshot = FALSE;
3502
3503   // ---------- initialize level time for Supaplex engine ---------------------
3504   // Supaplex levels with time limit currently unsupported -- should be added
3505   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3506     level.time = 0;
3507
3508   // ---------- initialize flags for handling game actions --------------------
3509
3510   // set flags for game actions to default values
3511   game.use_key_actions = TRUE;
3512   game.use_mouse_actions = FALSE;
3513
3514   // when using Mirror Magic game engine, handle mouse events only
3515   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
3516   {
3517     game.use_key_actions = FALSE;
3518     game.use_mouse_actions = TRUE;
3519   }
3520
3521   // check for custom elements with mouse click events
3522   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3523   {
3524     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3525     {
3526       int element = EL_CUSTOM_START + i;
3527
3528       if (HAS_ANY_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
3529           HAS_ANY_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE) ||
3530           HAS_ANY_CHANGE_EVENT(element, CE_MOUSE_CLICKED_ON_X) ||
3531           HAS_ANY_CHANGE_EVENT(element, CE_MOUSE_PRESSED_ON_X))
3532         game.use_mouse_actions = TRUE;
3533     }
3534   }
3535 }
3536
3537 static int get_num_special_action(int element, int action_first,
3538                                   int action_last)
3539 {
3540   int num_special_action = 0;
3541   int i, j;
3542
3543   for (i = action_first; i <= action_last; i++)
3544   {
3545     boolean found = FALSE;
3546
3547     for (j = 0; j < NUM_DIRECTIONS; j++)
3548       if (el_act_dir2img(element, i, j) !=
3549           el_act_dir2img(element, ACTION_DEFAULT, j))
3550         found = TRUE;
3551
3552     if (found)
3553       num_special_action++;
3554     else
3555       break;
3556   }
3557
3558   return num_special_action;
3559 }
3560
3561
3562 // ============================================================================
3563 // InitGame()
3564 // ----------------------------------------------------------------------------
3565 // initialize and start new game
3566 // ============================================================================
3567
3568 #if DEBUG_INIT_PLAYER
3569 static void DebugPrintPlayerStatus(char *message)
3570 {
3571   int i;
3572
3573   if (!options.debug)
3574     return;
3575
3576   Debug("game:init:player", "%s:", message);
3577
3578   for (i = 0; i < MAX_PLAYERS; i++)
3579   {
3580     struct PlayerInfo *player = &stored_player[i];
3581
3582     Debug("game:init:player",
3583           "- player %d: present == %d, connected == %d [%d/%d], active == %d%s",
3584           i + 1,
3585           player->present,
3586           player->connected,
3587           player->connected_locally,
3588           player->connected_network,
3589           player->active,
3590           (local_player == player ? " (local player)" : ""));
3591   }
3592 }
3593 #endif
3594
3595 void InitGame(void)
3596 {
3597   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3598   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3599   int fade_mask = REDRAW_FIELD;
3600   boolean restarting = (game_status == GAME_MODE_PLAYING);
3601   boolean emulate_bd = TRUE;    // unless non-BOULDERDASH elements found
3602   boolean emulate_sp = TRUE;    // unless non-SUPAPLEX    elements found
3603   int initial_move_dir = MV_DOWN;
3604   int i, j, x, y;
3605
3606   // required here to update video display before fading (FIX THIS)
3607   DrawMaskedBorder(REDRAW_DOOR_2);
3608
3609   if (!game.restart_level)
3610     CloseDoor(DOOR_CLOSE_1);
3611
3612   if (restarting)
3613   {
3614     // force fading out global animations displayed during game play
3615     SetGameStatus(GAME_MODE_PSEUDO_RESTARTING);
3616   }
3617   else
3618   {
3619     SetGameStatus(GAME_MODE_PLAYING);
3620   }
3621
3622   if (level_editor_test_game)
3623     FadeSkipNextFadeOut();
3624   else
3625     FadeSetEnterScreen();
3626
3627   if (CheckFadeAll())
3628     fade_mask = REDRAW_ALL;
3629
3630   FadeLevelSoundsAndMusic();
3631
3632   ExpireSoundLoops(TRUE);
3633
3634   FadeOut(fade_mask);
3635
3636   if (restarting)
3637   {
3638     // force restarting global animations displayed during game play
3639     RestartGlobalAnimsByStatus(GAME_MODE_PSEUDO_RESTARTING);
3640
3641     // this is required for "transforming" fade modes like cross-fading
3642     // (else global animations will be stopped, but not restarted here)
3643     SetAnimStatusBeforeFading(GAME_MODE_PSEUDO_RESTARTING);
3644
3645     SetGameStatus(GAME_MODE_PLAYING);
3646   }
3647
3648   if (level_editor_test_game)
3649     FadeSkipNextFadeIn();
3650
3651   // needed if different viewport properties defined for playing
3652   ChangeViewportPropertiesIfNeeded();
3653
3654   ClearField();
3655
3656   DrawCompleteVideoDisplay();
3657
3658   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3659
3660   InitGameEngine();
3661   InitGameControlValues();
3662
3663   if (tape.recording)
3664   {
3665     // initialize tape actions from game when recording tape
3666     tape.use_key_actions   = game.use_key_actions;
3667     tape.use_mouse_actions = game.use_mouse_actions;
3668
3669     // initialize visible playfield size when recording tape (for team mode)
3670     tape.scr_fieldx = SCR_FIELDX;
3671     tape.scr_fieldy = SCR_FIELDY;
3672   }
3673
3674   // don't play tapes over network
3675   network_playing = (network.enabled && !tape.playing);
3676
3677   for (i = 0; i < MAX_PLAYERS; i++)
3678   {
3679     struct PlayerInfo *player = &stored_player[i];
3680
3681     player->index_nr = i;
3682     player->index_bit = (1 << i);
3683     player->element_nr = EL_PLAYER_1 + i;
3684
3685     player->present = FALSE;
3686     player->active = FALSE;
3687     player->mapped = FALSE;
3688
3689     player->killed = FALSE;
3690     player->reanimated = FALSE;
3691     player->buried = FALSE;
3692
3693     player->action = 0;
3694     player->effective_action = 0;
3695     player->programmed_action = 0;
3696     player->snap_action = 0;
3697
3698     player->mouse_action.lx = 0;
3699     player->mouse_action.ly = 0;
3700     player->mouse_action.button = 0;
3701     player->mouse_action.button_hint = 0;
3702
3703     player->effective_mouse_action.lx = 0;
3704     player->effective_mouse_action.ly = 0;
3705     player->effective_mouse_action.button = 0;
3706     player->effective_mouse_action.button_hint = 0;
3707
3708     for (j = 0; j < MAX_NUM_KEYS; j++)
3709       player->key[j] = FALSE;
3710
3711     player->num_white_keys = 0;
3712
3713     player->dynabomb_count = 0;
3714     player->dynabomb_size = 1;
3715     player->dynabombs_left = 0;
3716     player->dynabomb_xl = FALSE;
3717
3718     player->MovDir = initial_move_dir;
3719     player->MovPos = 0;
3720     player->GfxPos = 0;
3721     player->GfxDir = initial_move_dir;
3722     player->GfxAction = ACTION_DEFAULT;
3723     player->Frame = 0;
3724     player->StepFrame = 0;
3725
3726     player->initial_element = player->element_nr;
3727     player->artwork_element =
3728       (level.use_artwork_element[i] ? level.artwork_element[i] :
3729        player->element_nr);
3730     player->use_murphy = FALSE;
3731
3732     player->block_last_field = FALSE;   // initialized in InitPlayerField()
3733     player->block_delay_adjustment = 0; // initialized in InitPlayerField()
3734
3735     player->gravity = level.initial_player_gravity[i];
3736
3737     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3738
3739     player->actual_frame_counter.count = 0;
3740     player->actual_frame_counter.value = 1;
3741
3742     player->step_counter = 0;
3743
3744     player->last_move_dir = initial_move_dir;
3745
3746     player->is_active = FALSE;
3747
3748     player->is_waiting = FALSE;
3749     player->is_moving = FALSE;
3750     player->is_auto_moving = FALSE;
3751     player->is_digging = FALSE;
3752     player->is_snapping = FALSE;
3753     player->is_collecting = FALSE;
3754     player->is_pushing = FALSE;
3755     player->is_switching = FALSE;
3756     player->is_dropping = FALSE;
3757     player->is_dropping_pressed = FALSE;
3758
3759     player->is_bored = FALSE;
3760     player->is_sleeping = FALSE;
3761
3762     player->was_waiting = TRUE;
3763     player->was_moving = FALSE;
3764     player->was_snapping = FALSE;
3765     player->was_dropping = FALSE;
3766
3767     player->force_dropping = FALSE;
3768
3769     player->frame_counter_bored = -1;
3770     player->frame_counter_sleeping = -1;
3771
3772     player->anim_delay_counter = 0;
3773     player->post_delay_counter = 0;
3774
3775     player->dir_waiting = initial_move_dir;
3776     player->action_waiting = ACTION_DEFAULT;
3777     player->last_action_waiting = ACTION_DEFAULT;
3778     player->special_action_bored = ACTION_DEFAULT;
3779     player->special_action_sleeping = ACTION_DEFAULT;
3780
3781     player->switch_x = -1;
3782     player->switch_y = -1;
3783
3784     player->drop_x = -1;
3785     player->drop_y = -1;
3786
3787     player->show_envelope = 0;
3788
3789     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3790
3791     player->push_delay       = -1;      // initialized when pushing starts
3792     player->push_delay_value = game.initial_push_delay_value;
3793
3794     player->drop_delay = 0;
3795     player->drop_pressed_delay = 0;
3796
3797     player->last_jx = -1;
3798     player->last_jy = -1;
3799     player->jx = -1;
3800     player->jy = -1;
3801
3802     player->shield_normal_time_left = 0;
3803     player->shield_deadly_time_left = 0;
3804
3805     player->last_removed_element = EL_UNDEFINED;
3806
3807     player->inventory_infinite_element = EL_UNDEFINED;
3808     player->inventory_size = 0;
3809
3810     if (level.use_initial_inventory[i])
3811     {
3812       for (j = 0; j < level.initial_inventory_size[i]; j++)
3813       {
3814         int element = level.initial_inventory_content[i][j];
3815         int collect_count = element_info[element].collect_count_initial;
3816         int k;
3817
3818         if (!IS_CUSTOM_ELEMENT(element))
3819           collect_count = 1;
3820
3821         if (collect_count == 0)
3822           player->inventory_infinite_element = element;
3823         else
3824           for (k = 0; k < collect_count; k++)
3825             if (player->inventory_size < MAX_INVENTORY_SIZE)
3826               player->inventory_element[player->inventory_size++] = element;
3827       }
3828     }
3829
3830     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3831     SnapField(player, 0, 0);
3832
3833     map_player_action[i] = i;
3834   }
3835
3836   network_player_action_received = FALSE;
3837
3838   // initial null action
3839   if (network_playing)
3840     SendToServer_MovePlayer(MV_NONE);
3841
3842   FrameCounter = 0;
3843   TimeFrames = 0;
3844   TimePlayed = 0;
3845   TimeLeft = level.time;
3846   TapeTime = 0;
3847
3848   ScreenMovDir = MV_NONE;
3849   ScreenMovPos = 0;
3850   ScreenGfxPos = 0;
3851
3852   ScrollStepSize = 0;   // will be correctly initialized by ScrollScreen()
3853
3854   game.robot_wheel_x = -1;
3855   game.robot_wheel_y = -1;
3856
3857   game.exit_x = -1;
3858   game.exit_y = -1;
3859
3860   game.all_players_gone = FALSE;
3861
3862   game.LevelSolved = FALSE;
3863   game.GameOver = FALSE;
3864
3865   game.GamePlayed = !tape.playing;
3866
3867   game.LevelSolved_GameWon = FALSE;
3868   game.LevelSolved_GameEnd = FALSE;
3869   game.LevelSolved_SaveTape = FALSE;
3870   game.LevelSolved_SaveScore = FALSE;
3871
3872   game.LevelSolved_CountingTime = 0;
3873   game.LevelSolved_CountingScore = 0;
3874   game.LevelSolved_CountingHealth = 0;
3875
3876   game.RestartGameRequested = FALSE;
3877
3878   game.panel.active = TRUE;
3879
3880   game.no_level_time_limit = (level.time == 0);
3881   game.time_limit = (leveldir_current->time_limit && setup.time_limit);
3882
3883   game.yamyam_content_nr = 0;
3884   game.robot_wheel_active = FALSE;
3885   game.magic_wall_active = FALSE;
3886   game.magic_wall_time_left = 0;
3887   game.light_time_left = 0;
3888   game.timegate_time_left = 0;
3889   game.switchgate_pos = 0;
3890   game.wind_direction = level.wind_direction_initial;
3891
3892   game.time_final = 0;
3893   game.score_time_final = 0;
3894
3895   game.score = 0;
3896   game.score_final = 0;
3897
3898   game.health = MAX_HEALTH;
3899   game.health_final = MAX_HEALTH;
3900
3901   game.gems_still_needed = level.gems_needed;
3902   game.sokoban_fields_still_needed = 0;
3903   game.sokoban_objects_still_needed = 0;
3904   game.lights_still_needed = 0;
3905   game.players_still_needed = 0;
3906   game.friends_still_needed = 0;
3907
3908   game.lenses_time_left = 0;
3909   game.magnify_time_left = 0;
3910
3911   game.ball_active = level.ball_active_initial;
3912   game.ball_content_nr = 0;
3913
3914   game.explosions_delayed = TRUE;
3915
3916   game.envelope_active = FALSE;
3917
3918   // special case: set custom artwork setting to initial value
3919   game.use_masked_elements = game.use_masked_elements_initial;
3920
3921   for (i = 0; i < NUM_BELTS; i++)
3922   {
3923     game.belt_dir[i] = MV_NONE;
3924     game.belt_dir_nr[i] = 3;            // not moving, next moving left
3925   }
3926
3927   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3928     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3929
3930 #if DEBUG_INIT_PLAYER
3931   DebugPrintPlayerStatus("Player status at level initialization");
3932 #endif
3933
3934   SCAN_PLAYFIELD(x, y)
3935   {
3936     Tile[x][y] = Last[x][y] = level.field[x][y];
3937     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3938     ChangeDelay[x][y] = 0;
3939     ChangePage[x][y] = -1;
3940     CustomValue[x][y] = 0;              // initialized in InitField()
3941     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3942     AmoebaNr[x][y] = 0;
3943     WasJustMoving[x][y] = 0;
3944     WasJustFalling[x][y] = 0;
3945     CheckCollision[x][y] = 0;
3946     CheckImpact[x][y] = 0;
3947     Stop[x][y] = FALSE;
3948     Pushed[x][y] = FALSE;
3949
3950     ChangeCount[x][y] = 0;
3951     ChangeEvent[x][y] = -1;
3952
3953     ExplodePhase[x][y] = 0;
3954     ExplodeDelay[x][y] = 0;
3955     ExplodeField[x][y] = EX_TYPE_NONE;
3956
3957     RunnerVisit[x][y] = 0;
3958     PlayerVisit[x][y] = 0;
3959
3960     GfxFrame[x][y] = 0;
3961     GfxRandom[x][y] = INIT_GFX_RANDOM();
3962     GfxRandomStatic[x][y] = INIT_GFX_RANDOM();
3963     GfxElement[x][y] = EL_UNDEFINED;
3964     GfxElementEmpty[x][y] = EL_EMPTY;
3965     GfxAction[x][y] = ACTION_DEFAULT;
3966     GfxDir[x][y] = MV_NONE;
3967     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3968   }
3969
3970   SCAN_PLAYFIELD(x, y)
3971   {
3972     if (emulate_bd && !IS_BD_ELEMENT(Tile[x][y]))
3973       emulate_bd = FALSE;
3974     if (emulate_sp && !IS_SP_ELEMENT(Tile[x][y]))
3975       emulate_sp = FALSE;
3976
3977     InitField(x, y, TRUE);
3978
3979     ResetGfxAnimation(x, y);
3980   }
3981
3982   InitBeltMovement();
3983
3984   // required if level does not contain any "empty space" element
3985   if (element_info[EL_EMPTY].use_gfx_element)
3986     game.use_masked_elements = TRUE;
3987
3988   for (i = 0; i < MAX_PLAYERS; i++)
3989   {
3990     struct PlayerInfo *player = &stored_player[i];
3991
3992     // set number of special actions for bored and sleeping animation
3993     player->num_special_action_bored =
3994       get_num_special_action(player->artwork_element,
3995                              ACTION_BORING_1, ACTION_BORING_LAST);
3996     player->num_special_action_sleeping =
3997       get_num_special_action(player->artwork_element,
3998                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3999   }
4000
4001   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
4002                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
4003
4004   // initialize type of slippery elements
4005   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4006   {
4007     if (!IS_CUSTOM_ELEMENT(i))
4008     {
4009       // default: elements slip down either to the left or right randomly
4010       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
4011
4012       // SP style elements prefer to slip down on the left side
4013       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
4014         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
4015
4016       // BD style elements prefer to slip down on the left side
4017       if (game.emulation == EMU_BOULDERDASH)
4018         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
4019     }
4020   }
4021
4022   // initialize explosion and ignition delay
4023   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4024   {
4025     if (!IS_CUSTOM_ELEMENT(i))
4026     {
4027       int num_phase = 8;
4028       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
4029                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
4030                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
4031       int last_phase = (num_phase + 1) * delay;
4032       int half_phase = (num_phase / 2) * delay;
4033
4034       element_info[i].explosion_delay = last_phase - 1;
4035       element_info[i].ignition_delay = half_phase;
4036
4037       if (i == EL_BLACK_ORB)
4038         element_info[i].ignition_delay = 1;
4039     }
4040   }
4041
4042   // correct non-moving belts to start moving left
4043   for (i = 0; i < NUM_BELTS; i++)
4044     if (game.belt_dir[i] == MV_NONE)
4045       game.belt_dir_nr[i] = 3;          // not moving, next moving left
4046
4047 #if USE_NEW_PLAYER_ASSIGNMENTS
4048   // use preferred player also in local single-player mode
4049   if (!network.enabled && !game.team_mode)
4050   {
4051     int new_index_nr = setup.network_player_nr;
4052
4053     if (new_index_nr >= 0 && new_index_nr < MAX_PLAYERS)
4054     {
4055       for (i = 0; i < MAX_PLAYERS; i++)
4056         stored_player[i].connected_locally = FALSE;
4057
4058       stored_player[new_index_nr].connected_locally = TRUE;
4059     }
4060   }
4061
4062   for (i = 0; i < MAX_PLAYERS; i++)
4063   {
4064     stored_player[i].connected = FALSE;
4065
4066     // in network game mode, the local player might not be the first player
4067     if (stored_player[i].connected_locally)
4068       local_player = &stored_player[i];
4069   }
4070
4071   if (!network.enabled)
4072     local_player->connected = TRUE;
4073
4074   if (tape.playing)
4075   {
4076     for (i = 0; i < MAX_PLAYERS; i++)
4077       stored_player[i].connected = tape.player_participates[i];
4078   }
4079   else if (network.enabled)
4080   {
4081     // add team mode players connected over the network (needed for correct
4082     // assignment of player figures from level to locally playing players)
4083
4084     for (i = 0; i < MAX_PLAYERS; i++)
4085       if (stored_player[i].connected_network)
4086         stored_player[i].connected = TRUE;
4087   }
4088   else if (game.team_mode)
4089   {
4090     // try to guess locally connected team mode players (needed for correct
4091     // assignment of player figures from level to locally playing players)
4092
4093     for (i = 0; i < MAX_PLAYERS; i++)
4094       if (setup.input[i].use_joystick ||
4095           setup.input[i].key.left != KSYM_UNDEFINED)
4096         stored_player[i].connected = TRUE;
4097   }
4098
4099 #if DEBUG_INIT_PLAYER
4100   DebugPrintPlayerStatus("Player status after level initialization");
4101 #endif
4102
4103 #if DEBUG_INIT_PLAYER
4104   Debug("game:init:player", "Reassigning players ...");
4105 #endif
4106
4107   // check if any connected player was not found in playfield
4108   for (i = 0; i < MAX_PLAYERS; i++)
4109   {
4110     struct PlayerInfo *player = &stored_player[i];
4111
4112     if (player->connected && !player->present)
4113     {
4114       struct PlayerInfo *field_player = NULL;
4115
4116 #if DEBUG_INIT_PLAYER
4117       Debug("game:init:player",
4118             "- looking for field player for player %d ...", i + 1);
4119 #endif
4120
4121       // assign first free player found that is present in the playfield
4122
4123       // first try: look for unmapped playfield player that is not connected
4124       for (j = 0; j < MAX_PLAYERS; j++)
4125         if (field_player == NULL &&
4126             stored_player[j].present &&
4127             !stored_player[j].mapped &&
4128             !stored_player[j].connected)
4129           field_player = &stored_player[j];
4130
4131       // second try: look for *any* unmapped playfield player
4132       for (j = 0; j < MAX_PLAYERS; j++)
4133         if (field_player == NULL &&
4134             stored_player[j].present &&
4135             !stored_player[j].mapped)
4136           field_player = &stored_player[j];
4137
4138       if (field_player != NULL)
4139       {
4140         int jx = field_player->jx, jy = field_player->jy;
4141
4142 #if DEBUG_INIT_PLAYER
4143         Debug("game:init:player", "- found player %d",
4144               field_player->index_nr + 1);
4145 #endif
4146
4147         player->present = FALSE;
4148         player->active = FALSE;
4149
4150         field_player->present = TRUE;
4151         field_player->active = TRUE;
4152
4153         /*
4154         player->initial_element = field_player->initial_element;
4155         player->artwork_element = field_player->artwork_element;
4156
4157         player->block_last_field       = field_player->block_last_field;
4158         player->block_delay_adjustment = field_player->block_delay_adjustment;
4159         */
4160
4161         StorePlayer[jx][jy] = field_player->element_nr;
4162
4163         field_player->jx = field_player->last_jx = jx;
4164         field_player->jy = field_player->last_jy = jy;
4165
4166         if (local_player == player)
4167           local_player = field_player;
4168
4169         map_player_action[field_player->index_nr] = i;
4170
4171         field_player->mapped = TRUE;
4172
4173 #if DEBUG_INIT_PLAYER
4174         Debug("game:init:player", "- map_player_action[%d] == %d",
4175               field_player->index_nr + 1, i + 1);
4176 #endif
4177       }
4178     }
4179
4180     if (player->connected && player->present)
4181       player->mapped = TRUE;
4182   }
4183
4184 #if DEBUG_INIT_PLAYER
4185   DebugPrintPlayerStatus("Player status after player assignment (first stage)");
4186 #endif
4187
4188 #else
4189
4190   // check if any connected player was not found in playfield
4191   for (i = 0; i < MAX_PLAYERS; i++)
4192   {
4193     struct PlayerInfo *player = &stored_player[i];
4194
4195     if (player->connected && !player->present)
4196     {
4197       for (j = 0; j < MAX_PLAYERS; j++)
4198       {
4199         struct PlayerInfo *field_player = &stored_player[j];
4200         int jx = field_player->jx, jy = field_player->jy;
4201
4202         // assign first free player found that is present in the playfield
4203         if (field_player->present && !field_player->connected)
4204         {
4205           player->present = TRUE;
4206           player->active = TRUE;
4207
4208           field_player->present = FALSE;
4209           field_player->active = FALSE;
4210
4211           player->initial_element = field_player->initial_element;
4212           player->artwork_element = field_player->artwork_element;
4213
4214           player->block_last_field       = field_player->block_last_field;
4215           player->block_delay_adjustment = field_player->block_delay_adjustment;
4216
4217           StorePlayer[jx][jy] = player->element_nr;
4218
4219           player->jx = player->last_jx = jx;
4220           player->jy = player->last_jy = jy;
4221
4222           break;
4223         }
4224       }
4225     }
4226   }
4227 #endif
4228
4229 #if 0
4230   Debug("game:init:player", "local_player->present == %d",
4231         local_player->present);
4232 #endif
4233
4234   // set focus to local player for network games, else to all players
4235   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
4236   game.centered_player_nr_next = game.centered_player_nr;
4237   game.set_centered_player = FALSE;
4238   game.set_centered_player_wrap = FALSE;
4239
4240   if (network_playing && tape.recording)
4241   {
4242     // store client dependent player focus when recording network games
4243     tape.centered_player_nr_next = game.centered_player_nr_next;
4244     tape.set_centered_player = TRUE;
4245   }
4246
4247   if (tape.playing)
4248   {
4249     // when playing a tape, eliminate all players who do not participate
4250
4251 #if USE_NEW_PLAYER_ASSIGNMENTS
4252
4253     if (!game.team_mode)
4254     {
4255       for (i = 0; i < MAX_PLAYERS; i++)
4256       {
4257         if (stored_player[i].active &&
4258             !tape.player_participates[map_player_action[i]])
4259         {
4260           struct PlayerInfo *player = &stored_player[i];
4261           int jx = player->jx, jy = player->jy;
4262
4263 #if DEBUG_INIT_PLAYER
4264           Debug("game:init:player", "Removing player %d at (%d, %d)",
4265                 i + 1, jx, jy);
4266 #endif
4267
4268           player->active = FALSE;
4269           StorePlayer[jx][jy] = 0;
4270           Tile[jx][jy] = EL_EMPTY;
4271         }
4272       }
4273     }
4274
4275 #else
4276
4277     for (i = 0; i < MAX_PLAYERS; i++)
4278     {
4279       if (stored_player[i].active &&
4280           !tape.player_participates[i])
4281       {
4282         struct PlayerInfo *player = &stored_player[i];
4283         int jx = player->jx, jy = player->jy;
4284
4285         player->active = FALSE;
4286         StorePlayer[jx][jy] = 0;
4287         Tile[jx][jy] = EL_EMPTY;
4288       }
4289     }
4290 #endif
4291   }
4292   else if (!network.enabled && !game.team_mode)         // && !tape.playing
4293   {
4294     // when in single player mode, eliminate all but the local player
4295
4296     for (i = 0; i < MAX_PLAYERS; i++)
4297     {
4298       struct PlayerInfo *player = &stored_player[i];
4299
4300       if (player->active && player != local_player)
4301       {
4302         int jx = player->jx, jy = player->jy;
4303
4304         player->active = FALSE;
4305         player->present = FALSE;
4306
4307         StorePlayer[jx][jy] = 0;
4308         Tile[jx][jy] = EL_EMPTY;
4309       }
4310     }
4311   }
4312
4313   for (i = 0; i < MAX_PLAYERS; i++)
4314     if (stored_player[i].active)
4315       game.players_still_needed++;
4316
4317   if (level.solved_by_one_player)
4318     game.players_still_needed = 1;
4319
4320   // when recording the game, store which players take part in the game
4321   if (tape.recording)
4322   {
4323 #if USE_NEW_PLAYER_ASSIGNMENTS
4324     for (i = 0; i < MAX_PLAYERS; i++)
4325       if (stored_player[i].connected)
4326         tape.player_participates[i] = TRUE;
4327 #else
4328     for (i = 0; i < MAX_PLAYERS; i++)
4329       if (stored_player[i].active)
4330         tape.player_participates[i] = TRUE;
4331 #endif
4332   }
4333
4334 #if DEBUG_INIT_PLAYER
4335   DebugPrintPlayerStatus("Player status after player assignment (final stage)");
4336 #endif
4337
4338   if (BorderElement == EL_EMPTY)
4339   {
4340     SBX_Left = 0;
4341     SBX_Right = lev_fieldx - SCR_FIELDX;
4342     SBY_Upper = 0;
4343     SBY_Lower = lev_fieldy - SCR_FIELDY;
4344   }
4345   else
4346   {
4347     SBX_Left = -1;
4348     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4349     SBY_Upper = -1;
4350     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4351   }
4352
4353   if (full_lev_fieldx <= SCR_FIELDX)
4354     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4355   if (full_lev_fieldy <= SCR_FIELDY)
4356     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4357
4358   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4359     SBX_Left--;
4360   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4361     SBY_Upper--;
4362
4363   // if local player not found, look for custom element that might create
4364   // the player (make some assumptions about the right custom element)
4365   if (!local_player->present)
4366   {
4367     int start_x = 0, start_y = 0;
4368     int found_rating = 0;
4369     int found_element = EL_UNDEFINED;
4370     int player_nr = local_player->index_nr;
4371
4372     SCAN_PLAYFIELD(x, y)
4373     {
4374       int element = Tile[x][y];
4375       int content;
4376       int xx, yy;
4377       boolean is_player;
4378
4379       if (level.use_start_element[player_nr] &&
4380           level.start_element[player_nr] == element &&
4381           found_rating < 4)
4382       {
4383         start_x = x;
4384         start_y = y;
4385
4386         found_rating = 4;
4387         found_element = element;
4388       }
4389
4390       if (!IS_CUSTOM_ELEMENT(element))
4391         continue;
4392
4393       if (CAN_CHANGE(element))
4394       {
4395         for (i = 0; i < element_info[element].num_change_pages; i++)
4396         {
4397           // check for player created from custom element as single target
4398           content = element_info[element].change_page[i].target_element;
4399           is_player = IS_PLAYER_ELEMENT(content);
4400
4401           if (is_player && (found_rating < 3 ||
4402                             (found_rating == 3 && element < found_element)))
4403           {
4404             start_x = x;
4405             start_y = y;
4406
4407             found_rating = 3;
4408             found_element = element;
4409           }
4410         }
4411       }
4412
4413       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4414       {
4415         // check for player created from custom element as explosion content
4416         content = element_info[element].content.e[xx][yy];
4417         is_player = IS_PLAYER_ELEMENT(content);
4418
4419         if (is_player && (found_rating < 2 ||
4420                           (found_rating == 2 && element < found_element)))
4421         {
4422           start_x = x + xx - 1;
4423           start_y = y + yy - 1;
4424
4425           found_rating = 2;
4426           found_element = element;
4427         }
4428
4429         if (!CAN_CHANGE(element))
4430           continue;
4431
4432         for (i = 0; i < element_info[element].num_change_pages; i++)
4433         {
4434           // check for player created from custom element as extended target
4435           content =
4436             element_info[element].change_page[i].target_content.e[xx][yy];
4437
4438           is_player = IS_PLAYER_ELEMENT(content);
4439
4440           if (is_player && (found_rating < 1 ||
4441                             (found_rating == 1 && element < found_element)))
4442           {
4443             start_x = x + xx - 1;
4444             start_y = y + yy - 1;
4445
4446             found_rating = 1;
4447             found_element = element;
4448           }
4449         }
4450       }
4451     }
4452
4453     scroll_x = SCROLL_POSITION_X(start_x);
4454     scroll_y = SCROLL_POSITION_Y(start_y);
4455   }
4456   else
4457   {
4458     scroll_x = SCROLL_POSITION_X(local_player->jx);
4459     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4460   }
4461
4462   if (game.forced_scroll_x != ARG_UNDEFINED_VALUE)
4463     scroll_x = game.forced_scroll_x;
4464   if (game.forced_scroll_y != ARG_UNDEFINED_VALUE)
4465     scroll_y = game.forced_scroll_y;
4466
4467   // !!! FIX THIS (START) !!!
4468   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4469   {
4470     InitGameEngine_EM();
4471   }
4472   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4473   {
4474     InitGameEngine_SP();
4475   }
4476   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4477   {
4478     InitGameEngine_MM();
4479   }
4480   else
4481   {
4482     DrawLevel(REDRAW_FIELD);
4483     DrawAllPlayers();
4484
4485     // after drawing the level, correct some elements
4486     if (game.timegate_time_left == 0)
4487       CloseAllOpenTimegates();
4488   }
4489
4490   // blit playfield from scroll buffer to normal back buffer for fading in
4491   BlitScreenToBitmap(backbuffer);
4492   // !!! FIX THIS (END) !!!
4493
4494   DrawMaskedBorder(fade_mask);
4495
4496   FadeIn(fade_mask);
4497
4498 #if 1
4499   // full screen redraw is required at this point in the following cases:
4500   // - special editor door undrawn when game was started from level editor
4501   // - drawing area (playfield) was changed and has to be removed completely
4502   redraw_mask = REDRAW_ALL;
4503   BackToFront();
4504 #endif
4505
4506   if (!game.restart_level)
4507   {
4508     // copy default game door content to main double buffer
4509
4510     // !!! CHECK AGAIN !!!
4511     SetPanelBackground();
4512     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4513     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4514   }
4515
4516   SetPanelBackground();
4517   SetDrawBackgroundMask(REDRAW_DOOR_1);
4518
4519   UpdateAndDisplayGameControlValues();
4520
4521   if (!game.restart_level)
4522   {
4523     UnmapGameButtons();
4524     UnmapTapeButtons();
4525
4526     FreeGameButtons();
4527     CreateGameButtons();
4528
4529     MapGameButtons();
4530     MapTapeButtons();
4531
4532     // copy actual game door content to door double buffer for OpenDoor()
4533     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4534
4535     OpenDoor(DOOR_OPEN_ALL);
4536
4537     KeyboardAutoRepeatOffUnlessAutoplay();
4538
4539 #if DEBUG_INIT_PLAYER
4540     DebugPrintPlayerStatus("Player status (final)");
4541 #endif
4542   }
4543
4544   UnmapAllGadgets();
4545
4546   MapGameButtons();
4547   MapTapeButtons();
4548
4549   if (!game.restart_level && !tape.playing)
4550   {
4551     LevelStats_incPlayed(level_nr);
4552
4553     SaveLevelSetup_SeriesInfo();
4554   }
4555
4556   game.restart_level = FALSE;
4557   game.request_active = FALSE;
4558
4559   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4560     InitGameActions_MM();
4561
4562   SaveEngineSnapshotToListInitial();
4563
4564   if (!game.restart_level)
4565   {
4566     PlaySound(SND_GAME_STARTING);
4567
4568     if (setup.sound_music)
4569       PlayLevelMusic();
4570   }
4571
4572   SetPlayfieldMouseCursorEnabled(!game.use_mouse_actions);
4573 }
4574
4575 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4576                         int actual_player_x, int actual_player_y)
4577 {
4578   // this is used for non-R'n'D game engines to update certain engine values
4579
4580   // needed to determine if sounds are played within the visible screen area
4581   scroll_x = actual_scroll_x;
4582   scroll_y = actual_scroll_y;
4583
4584   // needed to get player position for "follow finger" playing input method
4585   local_player->jx = actual_player_x;
4586   local_player->jy = actual_player_y;
4587 }
4588
4589 void InitMovDir(int x, int y)
4590 {
4591   int i, element = Tile[x][y];
4592   static int xy[4][2] =
4593   {
4594     {  0, +1 },
4595     { +1,  0 },
4596     {  0, -1 },
4597     { -1,  0 }
4598   };
4599   static int direction[3][4] =
4600   {
4601     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4602     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4603     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4604   };
4605
4606   switch (element)
4607   {
4608     case EL_BUG_RIGHT:
4609     case EL_BUG_UP:
4610     case EL_BUG_LEFT:
4611     case EL_BUG_DOWN:
4612       Tile[x][y] = EL_BUG;
4613       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4614       break;
4615
4616     case EL_SPACESHIP_RIGHT:
4617     case EL_SPACESHIP_UP:
4618     case EL_SPACESHIP_LEFT:
4619     case EL_SPACESHIP_DOWN:
4620       Tile[x][y] = EL_SPACESHIP;
4621       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4622       break;
4623
4624     case EL_BD_BUTTERFLY_RIGHT:
4625     case EL_BD_BUTTERFLY_UP:
4626     case EL_BD_BUTTERFLY_LEFT:
4627     case EL_BD_BUTTERFLY_DOWN:
4628       Tile[x][y] = EL_BD_BUTTERFLY;
4629       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4630       break;
4631
4632     case EL_BD_FIREFLY_RIGHT:
4633     case EL_BD_FIREFLY_UP:
4634     case EL_BD_FIREFLY_LEFT:
4635     case EL_BD_FIREFLY_DOWN:
4636       Tile[x][y] = EL_BD_FIREFLY;
4637       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4638       break;
4639
4640     case EL_PACMAN_RIGHT:
4641     case EL_PACMAN_UP:
4642     case EL_PACMAN_LEFT:
4643     case EL_PACMAN_DOWN:
4644       Tile[x][y] = EL_PACMAN;
4645       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4646       break;
4647
4648     case EL_YAMYAM_LEFT:
4649     case EL_YAMYAM_RIGHT:
4650     case EL_YAMYAM_UP:
4651     case EL_YAMYAM_DOWN:
4652       Tile[x][y] = EL_YAMYAM;
4653       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4654       break;
4655
4656     case EL_SP_SNIKSNAK:
4657       MovDir[x][y] = MV_UP;
4658       break;
4659
4660     case EL_SP_ELECTRON:
4661       MovDir[x][y] = MV_LEFT;
4662       break;
4663
4664     case EL_MOLE_LEFT:
4665     case EL_MOLE_RIGHT:
4666     case EL_MOLE_UP:
4667     case EL_MOLE_DOWN:
4668       Tile[x][y] = EL_MOLE;
4669       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4670       break;
4671
4672     case EL_SPRING_LEFT:
4673     case EL_SPRING_RIGHT:
4674       Tile[x][y] = EL_SPRING;
4675       MovDir[x][y] = direction[2][element - EL_SPRING_LEFT];
4676       break;
4677
4678     default:
4679       if (IS_CUSTOM_ELEMENT(element))
4680       {
4681         struct ElementInfo *ei = &element_info[element];
4682         int move_direction_initial = ei->move_direction_initial;
4683         int move_pattern = ei->move_pattern;
4684
4685         if (move_direction_initial == MV_START_PREVIOUS)
4686         {
4687           if (MovDir[x][y] != MV_NONE)
4688             return;
4689
4690           move_direction_initial = MV_START_AUTOMATIC;
4691         }
4692
4693         if (move_direction_initial == MV_START_RANDOM)
4694           MovDir[x][y] = 1 << RND(4);
4695         else if (move_direction_initial & MV_ANY_DIRECTION)
4696           MovDir[x][y] = move_direction_initial;
4697         else if (move_pattern == MV_ALL_DIRECTIONS ||
4698                  move_pattern == MV_TURNING_LEFT ||
4699                  move_pattern == MV_TURNING_RIGHT ||
4700                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4701                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4702                  move_pattern == MV_TURNING_RANDOM)
4703           MovDir[x][y] = 1 << RND(4);
4704         else if (move_pattern == MV_HORIZONTAL)
4705           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4706         else if (move_pattern == MV_VERTICAL)
4707           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4708         else if (move_pattern & MV_ANY_DIRECTION)
4709           MovDir[x][y] = element_info[element].move_pattern;
4710         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4711                  move_pattern == MV_ALONG_RIGHT_SIDE)
4712         {
4713           // use random direction as default start direction
4714           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4715             MovDir[x][y] = 1 << RND(4);
4716
4717           for (i = 0; i < NUM_DIRECTIONS; i++)
4718           {
4719             int x1 = x + xy[i][0];
4720             int y1 = y + xy[i][1];
4721
4722             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4723             {
4724               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4725                 MovDir[x][y] = direction[0][i];
4726               else
4727                 MovDir[x][y] = direction[1][i];
4728
4729               break;
4730             }
4731           }
4732         }                
4733       }
4734       else
4735       {
4736         MovDir[x][y] = 1 << RND(4);
4737
4738         if (element != EL_BUG &&
4739             element != EL_SPACESHIP &&
4740             element != EL_BD_BUTTERFLY &&
4741             element != EL_BD_FIREFLY)
4742           break;
4743
4744         for (i = 0; i < NUM_DIRECTIONS; i++)
4745         {
4746           int x1 = x + xy[i][0];
4747           int y1 = y + xy[i][1];
4748
4749           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4750           {
4751             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4752             {
4753               MovDir[x][y] = direction[0][i];
4754               break;
4755             }
4756             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4757                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4758             {
4759               MovDir[x][y] = direction[1][i];
4760               break;
4761             }
4762           }
4763         }
4764       }
4765       break;
4766   }
4767
4768   GfxDir[x][y] = MovDir[x][y];
4769 }
4770
4771 void InitAmoebaNr(int x, int y)
4772 {
4773   int i;
4774   int group_nr = AmoebaNeighbourNr(x, y);
4775
4776   if (group_nr == 0)
4777   {
4778     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4779     {
4780       if (AmoebaCnt[i] == 0)
4781       {
4782         group_nr = i;
4783         break;
4784       }
4785     }
4786   }
4787
4788   AmoebaNr[x][y] = group_nr;
4789   AmoebaCnt[group_nr]++;
4790   AmoebaCnt2[group_nr]++;
4791 }
4792
4793 static void LevelSolved_SetFinalGameValues(void)
4794 {
4795   game.time_final = (game.no_level_time_limit ? TimePlayed : TimeLeft);
4796   game.score_time_final = (level.use_step_counter ? TimePlayed :
4797                            TimePlayed * FRAMES_PER_SECOND + TimeFrames);
4798
4799   game.score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4800                       game_em.lev->score :
4801                       level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4802                       game_mm.score :
4803                       game.score);
4804
4805   game.health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4806                        MM_HEALTH(game_mm.laser_overload_value) :
4807                        game.health);
4808
4809   game.LevelSolved_CountingTime = game.time_final;
4810   game.LevelSolved_CountingScore = game.score_final;
4811   game.LevelSolved_CountingHealth = game.health_final;
4812 }
4813
4814 static void LevelSolved_DisplayFinalGameValues(int time, int score, int health)
4815 {
4816   game.LevelSolved_CountingTime = time;
4817   game.LevelSolved_CountingScore = score;
4818   game.LevelSolved_CountingHealth = health;
4819
4820   game_panel_controls[GAME_PANEL_TIME].value = time;
4821   game_panel_controls[GAME_PANEL_SCORE].value = score;
4822   game_panel_controls[GAME_PANEL_HEALTH].value = health;
4823
4824   DisplayGameControlValues();
4825 }
4826
4827 static void LevelSolved(void)
4828 {
4829   if (level.game_engine_type == GAME_ENGINE_TYPE_RND &&
4830       game.players_still_needed > 0)
4831     return;
4832
4833   game.LevelSolved = TRUE;
4834   game.GameOver = TRUE;
4835
4836   tape.solved = TRUE;
4837
4838   // needed here to display correct panel values while player walks into exit
4839   LevelSolved_SetFinalGameValues();
4840 }
4841
4842 void GameWon(void)
4843 {
4844   static int time_count_steps;
4845   static int time, time_final;
4846   static float score, score_final; // needed for time score < 10 for 10 seconds
4847   static int health, health_final;
4848   static int game_over_delay_1 = 0;
4849   static int game_over_delay_2 = 0;
4850   static int game_over_delay_3 = 0;
4851   int time_score_base = MIN(MAX(1, level.time_score_base), 10);
4852   float time_score = (float)level.score[SC_TIME_BONUS] / time_score_base;
4853
4854   if (!game.LevelSolved_GameWon)
4855   {
4856     int i;
4857
4858     // do not start end game actions before the player stops moving (to exit)
4859     if (local_player->active && local_player->MovPos)
4860       return;
4861
4862     // calculate final game values after player finished walking into exit
4863     LevelSolved_SetFinalGameValues();
4864
4865     game.LevelSolved_GameWon = TRUE;
4866     game.LevelSolved_SaveTape = tape.recording;
4867     game.LevelSolved_SaveScore = !tape.playing;
4868
4869     if (!tape.playing)
4870     {
4871       LevelStats_incSolved(level_nr);
4872
4873       SaveLevelSetup_SeriesInfo();
4874     }
4875
4876     if (tape.auto_play)         // tape might already be stopped here
4877       tape.auto_play_level_solved = TRUE;
4878
4879     TapeStop();
4880
4881     game_over_delay_1 = FRAMES_PER_SECOND;      // delay before counting time
4882     game_over_delay_2 = FRAMES_PER_SECOND / 2;  // delay before counting health
4883     game_over_delay_3 = FRAMES_PER_SECOND;      // delay before ending the game
4884
4885     time = time_final = game.time_final;
4886     score = score_final = game.score_final;
4887     health = health_final = game.health_final;
4888
4889     // update game panel values before (delayed) counting of score (if any)
4890     LevelSolved_DisplayFinalGameValues(time, score, health);
4891
4892     // if level has time score defined, calculate new final game values
4893     if (time_score > 0)
4894     {
4895       int time_final_max = 999;
4896       int time_frames_final_max = time_final_max * FRAMES_PER_SECOND;
4897       int time_frames = 0;
4898       int time_frames_left = TimeLeft * FRAMES_PER_SECOND - TimeFrames;
4899       int time_frames_played = TimePlayed * FRAMES_PER_SECOND + TimeFrames;
4900
4901       if (TimeLeft > 0)
4902       {
4903         time_final = 0;
4904         time_frames = time_frames_left;
4905       }
4906       else if (game.no_level_time_limit && TimePlayed < time_final_max)
4907       {
4908         time_final = time_final_max;
4909         time_frames = time_frames_final_max - time_frames_played;
4910       }
4911
4912       score_final += time_score * time_frames / FRAMES_PER_SECOND + 0.5;
4913
4914       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4915
4916       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4917       {
4918         health_final = 0;
4919         score_final += health * time_score;
4920       }
4921
4922       game.score_final = score_final;
4923       game.health_final = health_final;
4924     }
4925
4926     // if not counting score after game, immediately update game panel values
4927     if (level_editor_test_game || !setup.count_score_after_game)
4928     {
4929       time = time_final;
4930       score = score_final;
4931
4932       LevelSolved_DisplayFinalGameValues(time, score, health);
4933     }
4934
4935     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4936     {
4937       // check if last player has left the level
4938       if (game.exit_x >= 0 &&
4939           game.exit_y >= 0)
4940       {
4941         int x = game.exit_x;
4942         int y = game.exit_y;
4943         int element = Tile[x][y];
4944
4945         // close exit door after last player
4946         if ((game.all_players_gone &&
4947              (element == EL_EXIT_OPEN ||
4948               element == EL_SP_EXIT_OPEN ||
4949               element == EL_STEEL_EXIT_OPEN)) ||
4950             element == EL_EM_EXIT_OPEN ||
4951             element == EL_EM_STEEL_EXIT_OPEN)
4952         {
4953
4954           Tile[x][y] =
4955             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4956              element == EL_EM_EXIT_OPEN         ? EL_EM_EXIT_CLOSING :
4957              element == EL_SP_EXIT_OPEN         ? EL_SP_EXIT_CLOSING:
4958              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4959              EL_EM_STEEL_EXIT_CLOSING);
4960
4961           PlayLevelSoundElementAction(x, y, element, ACTION_CLOSING);
4962         }
4963
4964         // player disappears
4965         DrawLevelField(x, y);
4966       }
4967
4968       for (i = 0; i < MAX_PLAYERS; i++)
4969       {
4970         struct PlayerInfo *player = &stored_player[i];
4971
4972         if (player->present)
4973         {
4974           RemovePlayer(player);
4975
4976           // player disappears
4977           DrawLevelField(player->jx, player->jy);
4978         }
4979       }
4980     }
4981
4982     PlaySound(SND_GAME_WINNING);
4983   }
4984
4985   if (setup.count_score_after_game)
4986   {
4987     if (time != time_final)
4988     {
4989       if (game_over_delay_1 > 0)
4990       {
4991         game_over_delay_1--;
4992
4993         return;
4994       }
4995
4996       int time_to_go = ABS(time_final - time);
4997       int time_count_dir = (time < time_final ? +1 : -1);
4998
4999       if (time_to_go < time_count_steps)
5000         time_count_steps = 1;
5001
5002       time  += time_count_steps * time_count_dir;
5003       score += time_count_steps * time_score;
5004
5005       // set final score to correct rounding differences after counting score
5006       if (time == time_final)
5007         score = score_final;
5008
5009       LevelSolved_DisplayFinalGameValues(time, score, health);
5010
5011       if (time == time_final)
5012         StopSound(SND_GAME_LEVELTIME_BONUS);
5013       else if (setup.sound_loops)
5014         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
5015       else
5016         PlaySound(SND_GAME_LEVELTIME_BONUS);
5017
5018       return;
5019     }
5020
5021     if (health != health_final)
5022     {
5023       if (game_over_delay_2 > 0)
5024       {
5025         game_over_delay_2--;
5026
5027         return;
5028       }
5029
5030       int health_count_dir = (health < health_final ? +1 : -1);
5031
5032       health += health_count_dir;
5033       score  += time_score;
5034
5035       LevelSolved_DisplayFinalGameValues(time, score, health);
5036
5037       if (health == health_final)
5038         StopSound(SND_GAME_LEVELTIME_BONUS);
5039       else if (setup.sound_loops)
5040         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
5041       else
5042         PlaySound(SND_GAME_LEVELTIME_BONUS);
5043
5044       return;
5045     }
5046   }
5047
5048   game.panel.active = FALSE;
5049
5050   if (game_over_delay_3 > 0)
5051   {
5052     game_over_delay_3--;
5053
5054     return;
5055   }
5056
5057   GameEnd();
5058 }
5059
5060 void GameEnd(void)
5061 {
5062   // used instead of "level_nr" (needed for network games)
5063   int last_level_nr = levelset.level_nr;
5064   boolean tape_saved = FALSE;
5065
5066   game.LevelSolved_GameEnd = TRUE;
5067
5068   if (game.LevelSolved_SaveTape && !score_info_tape_play)
5069   {
5070     // make sure that request dialog to save tape does not open door again
5071     if (!global.use_envelope_request)
5072       CloseDoor(DOOR_CLOSE_1);
5073
5074     // ask to save tape
5075     tape_saved = SaveTapeChecked_LevelSolved(tape.level_nr);
5076
5077     // set unique basename for score tape (also saved in high score table)
5078     strcpy(tape.score_tape_basename, getScoreTapeBasename(setup.player_name));
5079   }
5080
5081   // if no tape is to be saved, close both doors simultaneously
5082   CloseDoor(DOOR_CLOSE_ALL);
5083
5084   if (level_editor_test_game || score_info_tape_play)
5085   {
5086     SetGameStatus(GAME_MODE_MAIN);
5087
5088     DrawMainMenu();
5089
5090     return;
5091   }
5092
5093   if (!game.LevelSolved_SaveScore)
5094   {
5095     SetGameStatus(GAME_MODE_MAIN);
5096
5097     DrawMainMenu();
5098
5099     return;
5100   }
5101
5102   if (level_nr == leveldir_current->handicap_level)
5103   {
5104     leveldir_current->handicap_level++;
5105
5106     SaveLevelSetup_SeriesInfo();
5107   }
5108
5109   // save score and score tape before potentially erasing tape below
5110   NewHighScore(last_level_nr, tape_saved);
5111
5112   if (setup.increment_levels &&
5113       level_nr < leveldir_current->last_level &&
5114       !network_playing)
5115   {
5116     level_nr++;         // advance to next level
5117     TapeErase();        // start with empty tape
5118
5119     if (setup.auto_play_next_level)
5120     {
5121       scores.continue_playing = TRUE;
5122       scores.next_level_nr = level_nr;
5123
5124       LoadLevel(level_nr);
5125
5126       SaveLevelSetup_SeriesInfo();
5127     }
5128   }
5129
5130   if (scores.last_added >= 0 && setup.show_scores_after_game)
5131   {
5132     SetGameStatus(GAME_MODE_SCORES);
5133
5134     DrawHallOfFame(last_level_nr);
5135   }
5136   else if (scores.continue_playing)
5137   {
5138     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
5139   }
5140   else
5141   {
5142     SetGameStatus(GAME_MODE_MAIN);
5143
5144     DrawMainMenu();
5145   }
5146 }
5147
5148 static int addScoreEntry(struct ScoreInfo *list, struct ScoreEntry *new_entry,
5149                          boolean one_score_entry_per_name)
5150 {
5151   int i;
5152
5153   if (strEqual(new_entry->name, EMPTY_PLAYER_NAME))
5154     return -1;
5155
5156   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
5157   {
5158     struct ScoreEntry *entry = &list->entry[i];
5159     boolean score_is_better = (new_entry->score >  entry->score);
5160     boolean score_is_equal  = (new_entry->score == entry->score);
5161     boolean time_is_better  = (new_entry->time  <  entry->time);
5162     boolean time_is_equal   = (new_entry->time  == entry->time);
5163     boolean better_by_score = (score_is_better ||
5164                                (score_is_equal && time_is_better));
5165     boolean better_by_time  = (time_is_better ||
5166                                (time_is_equal && score_is_better));
5167     boolean is_better = (level.rate_time_over_score ? better_by_time :
5168                          better_by_score);
5169     boolean entry_is_empty = (entry->score == 0 &&
5170                               entry->time == 0);
5171
5172     // prevent adding server score entries if also existing in local score file
5173     // (special case: historic score entries have an empty tape basename entry)
5174     if (strEqual(new_entry->tape_basename, entry->tape_basename) &&
5175         !strEqual(new_entry->tape_basename, UNDEFINED_FILENAME))
5176     {
5177       // add fields from server score entry not stored in local score entry
5178       // (currently, this means setting platform, version and country fields;
5179       // in rare cases, this may also correct an invalid score value, as
5180       // historic scores might have been truncated to 16-bit values locally)
5181       *entry = *new_entry;
5182
5183       return -1;
5184     }
5185
5186     if (is_better || entry_is_empty)
5187     {
5188       // player has made it to the hall of fame
5189
5190       if (i < MAX_SCORE_ENTRIES - 1)
5191       {
5192         int m = MAX_SCORE_ENTRIES - 1;
5193         int l;
5194
5195         if (one_score_entry_per_name)
5196         {
5197           for (l = i; l < MAX_SCORE_ENTRIES; l++)
5198             if (strEqual(list->entry[l].name, new_entry->name))
5199               m = l;
5200
5201           if (m == i)   // player's new highscore overwrites his old one
5202             goto put_into_list;
5203         }
5204
5205         for (l = m; l > i; l--)
5206           list->entry[l] = list->entry[l - 1];
5207       }
5208
5209       put_into_list:
5210
5211       *entry = *new_entry;
5212
5213       return i;
5214     }
5215     else if (one_score_entry_per_name &&
5216              strEqual(entry->name, new_entry->name))
5217     {
5218       // player already in high score list with better score or time
5219
5220       return -1;
5221     }
5222   }
5223
5224   // special case: new score is beyond the last high score list position
5225   return MAX_SCORE_ENTRIES;
5226 }
5227
5228 void NewHighScore(int level_nr, boolean tape_saved)
5229 {
5230   struct ScoreEntry new_entry = {{ 0 }}; // (prevent warning from GCC bug 53119)
5231   boolean one_per_name = FALSE;
5232
5233   strncpy(new_entry.tape_basename, tape.score_tape_basename, MAX_FILENAME_LEN);
5234   strncpy(new_entry.name, setup.player_name, MAX_PLAYER_NAME_LEN);
5235
5236   new_entry.score = game.score_final;
5237   new_entry.time = game.score_time_final;
5238
5239   LoadScore(level_nr);
5240
5241   scores.last_added = addScoreEntry(&scores, &new_entry, one_per_name);
5242
5243   if (scores.last_added >= MAX_SCORE_ENTRIES)
5244   {
5245     scores.last_added = MAX_SCORE_ENTRIES - 1;
5246     scores.force_last_added = TRUE;
5247
5248     scores.entry[scores.last_added] = new_entry;
5249
5250     // store last added local score entry (before merging server scores)
5251     scores.last_added_local = scores.last_added;
5252
5253     return;
5254   }
5255
5256   if (scores.last_added < 0)
5257     return;
5258
5259   SaveScore(level_nr);
5260
5261   // store last added local score entry (before merging server scores)
5262   scores.last_added_local = scores.last_added;
5263
5264   if (!game.LevelSolved_SaveTape)
5265     return;
5266
5267   SaveScoreTape(level_nr);
5268
5269   if (setup.ask_for_using_api_server)
5270   {
5271     setup.use_api_server =
5272       Request("Upload your score and tape to the high score server?", REQ_ASK);
5273
5274     if (!setup.use_api_server)
5275       Request("Not using high score server! Use setup menu to enable again!",
5276               REQ_CONFIRM);
5277
5278     runtime.use_api_server = setup.use_api_server;
5279
5280     // after asking for using API server once, do not ask again
5281     setup.ask_for_using_api_server = FALSE;
5282
5283     SaveSetup_ServerSetup();
5284   }
5285
5286   SaveServerScore(level_nr, tape_saved);
5287 }
5288
5289 void MergeServerScore(void)
5290 {
5291   struct ScoreEntry last_added_entry;
5292   boolean one_per_name = FALSE;
5293   int i;
5294
5295   if (scores.last_added >= 0)
5296     last_added_entry = scores.entry[scores.last_added];
5297
5298   for (i = 0; i < server_scores.num_entries; i++)
5299   {
5300     int pos = addScoreEntry(&scores, &server_scores.entry[i], one_per_name);
5301
5302     if (pos >= 0 && pos <= scores.last_added)
5303       scores.last_added++;
5304   }
5305
5306   if (scores.last_added >= MAX_SCORE_ENTRIES)
5307   {
5308     scores.last_added = MAX_SCORE_ENTRIES - 1;
5309     scores.force_last_added = TRUE;
5310
5311     scores.entry[scores.last_added] = last_added_entry;
5312   }
5313 }
5314
5315 static int getElementMoveStepsizeExt(int x, int y, int direction)
5316 {
5317   int element = Tile[x][y];
5318   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5319   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5320   int horiz_move = (dx != 0);
5321   int sign = (horiz_move ? dx : dy);
5322   int step = sign * element_info[element].move_stepsize;
5323
5324   // special values for move stepsize for spring and things on conveyor belt
5325   if (horiz_move)
5326   {
5327     if (CAN_FALL(element) &&
5328         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Tile[x][y + 1]))
5329       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5330     else if (element == EL_SPRING)
5331       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5332   }
5333
5334   return step;
5335 }
5336
5337 static int getElementMoveStepsize(int x, int y)
5338 {
5339   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5340 }
5341
5342 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5343 {
5344   if (player->GfxAction != action || player->GfxDir != dir)
5345   {
5346     player->GfxAction = action;
5347     player->GfxDir = dir;
5348     player->Frame = 0;
5349     player->StepFrame = 0;
5350   }
5351 }
5352
5353 static void ResetGfxFrame(int x, int y)
5354 {
5355   // profiling showed that "autotest" spends 10~20% of its time in this function
5356   if (DrawingDeactivatedField())
5357     return;
5358
5359   int element = Tile[x][y];
5360   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5361
5362   if (graphic_info[graphic].anim_global_sync)
5363     GfxFrame[x][y] = FrameCounter;
5364   else if (graphic_info[graphic].anim_global_anim_sync)
5365     GfxFrame[x][y] = getGlobalAnimSyncFrame();
5366   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5367     GfxFrame[x][y] = CustomValue[x][y];
5368   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5369     GfxFrame[x][y] = element_info[element].collect_score;
5370   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5371     GfxFrame[x][y] = ChangeDelay[x][y];
5372 }
5373
5374 static void ResetGfxAnimation(int x, int y)
5375 {
5376   GfxAction[x][y] = ACTION_DEFAULT;
5377   GfxDir[x][y] = MovDir[x][y];
5378   GfxFrame[x][y] = 0;
5379
5380   ResetGfxFrame(x, y);
5381 }
5382
5383 static void ResetRandomAnimationValue(int x, int y)
5384 {
5385   GfxRandom[x][y] = INIT_GFX_RANDOM();
5386 }
5387
5388 static void InitMovingField(int x, int y, int direction)
5389 {
5390   int element = Tile[x][y];
5391   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5392   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5393   int newx = x + dx;
5394   int newy = y + dy;
5395   boolean is_moving_before, is_moving_after;
5396
5397   // check if element was/is moving or being moved before/after mode change
5398   is_moving_before = (WasJustMoving[x][y] != 0);
5399   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5400
5401   // reset animation only for moving elements which change direction of moving
5402   // or which just started or stopped moving
5403   // (else CEs with property "can move" / "not moving" are reset each frame)
5404   if (is_moving_before != is_moving_after ||
5405       direction != MovDir[x][y])
5406     ResetGfxAnimation(x, y);
5407
5408   MovDir[x][y] = direction;
5409   GfxDir[x][y] = direction;
5410
5411   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5412                      direction == MV_DOWN && CAN_FALL(element) ?
5413                      ACTION_FALLING : ACTION_MOVING);
5414
5415   // this is needed for CEs with property "can move" / "not moving"
5416
5417   if (is_moving_after)
5418   {
5419     if (Tile[newx][newy] == EL_EMPTY)
5420       Tile[newx][newy] = EL_BLOCKED;
5421
5422     MovDir[newx][newy] = MovDir[x][y];
5423
5424     CustomValue[newx][newy] = CustomValue[x][y];
5425
5426     GfxFrame[newx][newy] = GfxFrame[x][y];
5427     GfxRandom[newx][newy] = GfxRandom[x][y];
5428     GfxAction[newx][newy] = GfxAction[x][y];
5429     GfxDir[newx][newy] = GfxDir[x][y];
5430   }
5431 }
5432
5433 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5434 {
5435   int direction = MovDir[x][y];
5436   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5437   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5438
5439   *goes_to_x = newx;
5440   *goes_to_y = newy;
5441 }
5442
5443 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5444 {
5445   int direction = MovDir[x][y];
5446   int oldx = x + (direction & MV_LEFT ? +1 : direction & MV_RIGHT ? -1 : 0);
5447   int oldy = y + (direction & MV_UP   ? +1 : direction & MV_DOWN  ? -1 : 0);
5448
5449   *comes_from_x = oldx;
5450   *comes_from_y = oldy;
5451 }
5452
5453 static int MovingOrBlocked2Element(int x, int y)
5454 {
5455   int element = Tile[x][y];
5456
5457   if (element == EL_BLOCKED)
5458   {
5459     int oldx, oldy;
5460
5461     Blocked2Moving(x, y, &oldx, &oldy);
5462
5463     return Tile[oldx][oldy];
5464   }
5465
5466   return element;
5467 }
5468
5469 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5470 {
5471   // like MovingOrBlocked2Element(), but if element is moving
5472   // and (x, y) is the field the moving element is just leaving,
5473   // return EL_BLOCKED instead of the element value
5474   int element = Tile[x][y];
5475
5476   if (IS_MOVING(x, y))
5477   {
5478     if (element == EL_BLOCKED)
5479     {
5480       int oldx, oldy;
5481
5482       Blocked2Moving(x, y, &oldx, &oldy);
5483       return Tile[oldx][oldy];
5484     }
5485     else
5486       return EL_BLOCKED;
5487   }
5488   else
5489     return element;
5490 }
5491
5492 static void RemoveField(int x, int y)
5493 {
5494   Tile[x][y] = EL_EMPTY;
5495
5496   MovPos[x][y] = 0;
5497   MovDir[x][y] = 0;
5498   MovDelay[x][y] = 0;
5499
5500   CustomValue[x][y] = 0;
5501
5502   AmoebaNr[x][y] = 0;
5503   ChangeDelay[x][y] = 0;
5504   ChangePage[x][y] = -1;
5505   Pushed[x][y] = FALSE;
5506
5507   GfxElement[x][y] = EL_UNDEFINED;
5508   GfxAction[x][y] = ACTION_DEFAULT;
5509   GfxDir[x][y] = MV_NONE;
5510 }
5511
5512 static void RemoveMovingField(int x, int y)
5513 {
5514   int oldx = x, oldy = y, newx = x, newy = y;
5515   int element = Tile[x][y];
5516   int next_element = EL_UNDEFINED;
5517
5518   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5519     return;
5520
5521   if (IS_MOVING(x, y))
5522   {
5523     Moving2Blocked(x, y, &newx, &newy);
5524
5525     if (Tile[newx][newy] != EL_BLOCKED)
5526     {
5527       // element is moving, but target field is not free (blocked), but
5528       // already occupied by something different (example: acid pool);
5529       // in this case, only remove the moving field, but not the target
5530
5531       RemoveField(oldx, oldy);
5532
5533       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5534
5535       TEST_DrawLevelField(oldx, oldy);
5536
5537       return;
5538     }
5539   }
5540   else if (element == EL_BLOCKED)
5541   {
5542     Blocked2Moving(x, y, &oldx, &oldy);
5543     if (!IS_MOVING(oldx, oldy))
5544       return;
5545   }
5546
5547   if (element == EL_BLOCKED &&
5548       (Tile[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5549        Tile[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5550        Tile[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5551        Tile[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5552        Tile[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5553        Tile[oldx][oldy] == EL_AMOEBA_DROPPING))
5554     next_element = get_next_element(Tile[oldx][oldy]);
5555
5556   RemoveField(oldx, oldy);
5557   RemoveField(newx, newy);
5558
5559   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5560
5561   if (next_element != EL_UNDEFINED)
5562     Tile[oldx][oldy] = next_element;
5563
5564   TEST_DrawLevelField(oldx, oldy);
5565   TEST_DrawLevelField(newx, newy);
5566 }
5567
5568 void DrawDynamite(int x, int y)
5569 {
5570   int sx = SCREENX(x), sy = SCREENY(y);
5571   int graphic = el2img(Tile[x][y]);
5572   int frame;
5573
5574   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5575     return;
5576
5577   if (IS_WALKABLE_INSIDE(Back[x][y]))
5578     return;
5579
5580   if (Back[x][y])
5581     DrawLevelElement(x, y, Back[x][y]);
5582   else if (Store[x][y])
5583     DrawLevelElement(x, y, Store[x][y]);
5584   else if (game.use_masked_elements)
5585     DrawLevelElement(x, y, EL_EMPTY);
5586
5587   frame = getGraphicAnimationFrameXY(graphic, x, y);
5588
5589   if (Back[x][y] || Store[x][y] || game.use_masked_elements)
5590     DrawGraphicThruMask(sx, sy, graphic, frame);
5591   else
5592     DrawGraphic(sx, sy, graphic, frame);
5593 }
5594
5595 static void CheckDynamite(int x, int y)
5596 {
5597   if (MovDelay[x][y] != 0)      // dynamite is still waiting to explode
5598   {
5599     MovDelay[x][y]--;
5600
5601     if (MovDelay[x][y] != 0)
5602     {
5603       DrawDynamite(x, y);
5604       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5605
5606       return;
5607     }
5608   }
5609
5610   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5611
5612   Bang(x, y);
5613 }
5614
5615 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5616 {
5617   boolean num_checked_players = 0;
5618   int i;
5619
5620   for (i = 0; i < MAX_PLAYERS; i++)
5621   {
5622     if (stored_player[i].active)
5623     {
5624       int sx = stored_player[i].jx;
5625       int sy = stored_player[i].jy;
5626
5627       if (num_checked_players == 0)
5628       {
5629         *sx1 = *sx2 = sx;
5630         *sy1 = *sy2 = sy;
5631       }
5632       else
5633       {
5634         *sx1 = MIN(*sx1, sx);
5635         *sy1 = MIN(*sy1, sy);
5636         *sx2 = MAX(*sx2, sx);
5637         *sy2 = MAX(*sy2, sy);
5638       }
5639
5640       num_checked_players++;
5641     }
5642   }
5643 }
5644
5645 static boolean checkIfAllPlayersFitToScreen_RND(void)
5646 {
5647   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5648
5649   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5650
5651   return (sx2 - sx1 < SCR_FIELDX &&
5652           sy2 - sy1 < SCR_FIELDY);
5653 }
5654
5655 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5656 {
5657   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5658
5659   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5660
5661   *sx = (sx1 + sx2) / 2;
5662   *sy = (sy1 + sy2) / 2;
5663 }
5664
5665 static void DrawRelocateScreen(int old_x, int old_y, int x, int y,
5666                                boolean center_screen, boolean quick_relocation)
5667 {
5668   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5669   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5670   boolean no_delay = (tape.warp_forward);
5671   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5672   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5673   int new_scroll_x, new_scroll_y;
5674
5675   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5676   {
5677     // case 1: quick relocation inside visible screen (without scrolling)
5678
5679     RedrawPlayfield();
5680
5681     return;
5682   }
5683
5684   if (!level.shifted_relocation || center_screen)
5685   {
5686     // relocation _with_ centering of screen
5687
5688     new_scroll_x = SCROLL_POSITION_X(x);
5689     new_scroll_y = SCROLL_POSITION_Y(y);
5690   }
5691   else
5692   {
5693     // relocation _without_ centering of screen
5694
5695     // apply distance between old and new player position to scroll position
5696     int shifted_scroll_x = scroll_x + (x - old_x);
5697     int shifted_scroll_y = scroll_y + (y - old_y);
5698
5699     // make sure that shifted scroll position does not scroll beyond screen
5700     new_scroll_x = SCROLL_POSITION_X(shifted_scroll_x + MIDPOSX);
5701     new_scroll_y = SCROLL_POSITION_Y(shifted_scroll_y + MIDPOSY);
5702
5703     // special case for teleporting from one end of the playfield to the other
5704     // (this kludge prevents the destination area to be shifted by half a tile
5705     // against the source destination for even screen width or screen height;
5706     // probably most useful when used with high "game.forced_scroll_delay_value"
5707     // in combination with "game.forced_scroll_x" and "game.forced_scroll_y")
5708     if (quick_relocation)
5709     {
5710       if (EVEN(SCR_FIELDX))
5711       {
5712         // relocate (teleport) between left and right border (half or full)
5713         if (scroll_x == SBX_Left && new_scroll_x == SBX_Right - 1)
5714           new_scroll_x = SBX_Right;
5715         else if (scroll_x == SBX_Left + 1 && new_scroll_x == SBX_Right)
5716           new_scroll_x = SBX_Right - 1;
5717         else if (scroll_x == SBX_Right && new_scroll_x == SBX_Left + 1)
5718           new_scroll_x = SBX_Left;
5719         else if (scroll_x == SBX_Right - 1 && new_scroll_x == SBX_Left)
5720           new_scroll_x = SBX_Left + 1;
5721       }
5722
5723       if (EVEN(SCR_FIELDY))
5724       {
5725         // relocate (teleport) between top and bottom border (half or full)
5726         if (scroll_y == SBY_Upper && new_scroll_y == SBY_Lower - 1)
5727           new_scroll_y = SBY_Lower;
5728         else if (scroll_y == SBY_Upper + 1 && new_scroll_y == SBY_Lower)
5729           new_scroll_y = SBY_Lower - 1;
5730         else if (scroll_y == SBY_Lower && new_scroll_y == SBY_Upper + 1)
5731           new_scroll_y = SBY_Upper;
5732         else if (scroll_y == SBY_Lower - 1 && new_scroll_y == SBY_Upper)
5733           new_scroll_y = SBY_Upper + 1;
5734       }
5735     }
5736   }
5737
5738   if (quick_relocation)
5739   {
5740     // case 2: quick relocation (redraw without visible scrolling)
5741
5742     scroll_x = new_scroll_x;
5743     scroll_y = new_scroll_y;
5744
5745     RedrawPlayfield();
5746
5747     return;
5748   }
5749
5750   // case 3: visible relocation (with scrolling to new position)
5751
5752   ScrollScreen(NULL, SCROLL_GO_ON);     // scroll last frame to full tile
5753
5754   SetVideoFrameDelay(wait_delay_value);
5755
5756   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5757   {
5758     int dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5759     int dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5760
5761     if (dx == 0 && dy == 0)             // no scrolling needed at all
5762       break;
5763
5764     scroll_x -= dx;
5765     scroll_y -= dy;
5766
5767     // set values for horizontal/vertical screen scrolling (half tile size)
5768     int dir_x = (dx != 0 ? MV_HORIZONTAL : 0);
5769     int dir_y = (dy != 0 ? MV_VERTICAL   : 0);
5770     int pos_x = dx * TILEX / 2;
5771     int pos_y = dy * TILEY / 2;
5772     int fx = getFieldbufferOffsetX_RND(dir_x, pos_x);
5773     int fy = getFieldbufferOffsetY_RND(dir_y, pos_y);
5774
5775     ScrollLevel(dx, dy);
5776     DrawAllPlayers();
5777
5778     // scroll in two steps of half tile size to make things smoother
5779     BlitScreenToBitmapExt_RND(window, fx, fy);
5780
5781     // scroll second step to align at full tile size
5782     BlitScreenToBitmap(window);
5783   }
5784
5785   DrawAllPlayers();
5786   BackToFront();
5787
5788   SetVideoFrameDelay(frame_delay_value_old);
5789 }
5790
5791 static void RelocatePlayer(int jx, int jy, int el_player_raw)
5792 {
5793   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5794   int player_nr = GET_PLAYER_NR(el_player);
5795   struct PlayerInfo *player = &stored_player[player_nr];
5796   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5797   boolean no_delay = (tape.warp_forward);
5798   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5799   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5800   int old_jx = player->jx;
5801   int old_jy = player->jy;
5802   int old_element = Tile[old_jx][old_jy];
5803   int element = Tile[jx][jy];
5804   boolean player_relocated = (old_jx != jx || old_jy != jy);
5805
5806   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5807   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5808   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5809   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5810   int leave_side_horiz = move_dir_horiz;
5811   int leave_side_vert  = move_dir_vert;
5812   int enter_side = enter_side_horiz | enter_side_vert;
5813   int leave_side = leave_side_horiz | leave_side_vert;
5814
5815   if (player->buried)           // do not reanimate dead player
5816     return;
5817
5818   if (!player_relocated)        // no need to relocate the player
5819     return;
5820
5821   if (IS_PLAYER(jx, jy))        // player already placed at new position
5822   {
5823     RemoveField(jx, jy);        // temporarily remove newly placed player
5824     DrawLevelField(jx, jy);
5825   }
5826
5827   if (player->present)
5828   {
5829     while (player->MovPos)
5830     {
5831       ScrollPlayer(player, SCROLL_GO_ON);
5832       ScrollScreen(NULL, SCROLL_GO_ON);
5833
5834       AdvanceFrameAndPlayerCounters(player->index_nr);
5835
5836       DrawPlayer(player);
5837
5838       BackToFront_WithFrameDelay(wait_delay_value);
5839     }
5840
5841     DrawPlayer(player);         // needed here only to cleanup last field
5842     DrawLevelField(player->jx, player->jy);     // remove player graphic
5843
5844     player->is_moving = FALSE;
5845   }
5846
5847   if (IS_CUSTOM_ELEMENT(old_element))
5848     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5849                                CE_LEFT_BY_PLAYER,
5850                                player->index_bit, leave_side);
5851
5852   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5853                                       CE_PLAYER_LEAVES_X,
5854                                       player->index_bit, leave_side);
5855
5856   Tile[jx][jy] = el_player;
5857   InitPlayerField(jx, jy, el_player, TRUE);
5858
5859   /* "InitPlayerField()" above sets Tile[jx][jy] to EL_EMPTY, but it may be
5860      possible that the relocation target field did not contain a player element,
5861      but a walkable element, to which the new player was relocated -- in this
5862      case, restore that (already initialized!) element on the player field */
5863   if (!IS_PLAYER_ELEMENT(element))      // player may be set on walkable element
5864   {
5865     Tile[jx][jy] = element;     // restore previously existing element
5866   }
5867
5868   // only visually relocate centered player
5869   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy,
5870                      FALSE, level.instant_relocation);
5871
5872   TestIfPlayerTouchesBadThing(jx, jy);
5873   TestIfPlayerTouchesCustomElement(jx, jy);
5874
5875   if (IS_CUSTOM_ELEMENT(element))
5876     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5877                                player->index_bit, enter_side);
5878
5879   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5880                                       player->index_bit, enter_side);
5881
5882   if (player->is_switching)
5883   {
5884     /* ensure that relocation while still switching an element does not cause
5885        a new element to be treated as also switched directly after relocation
5886        (this is important for teleporter switches that teleport the player to
5887        a place where another teleporter switch is in the same direction, which
5888        would then incorrectly be treated as immediately switched before the
5889        direction key that caused the switch was released) */
5890
5891     player->switch_x += jx - old_jx;
5892     player->switch_y += jy - old_jy;
5893   }
5894 }
5895
5896 static void Explode(int ex, int ey, int phase, int mode)
5897 {
5898   int x, y;
5899   int last_phase;
5900   int border_element;
5901
5902   if (game.explosions_delayed)
5903   {
5904     ExplodeField[ex][ey] = mode;
5905     return;
5906   }
5907
5908   if (phase == EX_PHASE_START)          // initialize 'Store[][]' field
5909   {
5910     int center_element = Tile[ex][ey];
5911     int ce_value = CustomValue[ex][ey];
5912     int ce_score = element_info[center_element].collect_score;
5913     int artwork_element, explosion_element;     // set these values later
5914
5915     // remove things displayed in background while burning dynamite
5916     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5917       Back[ex][ey] = 0;
5918
5919     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5920     {
5921       // put moving element to center field (and let it explode there)
5922       center_element = MovingOrBlocked2Element(ex, ey);
5923       RemoveMovingField(ex, ey);
5924       Tile[ex][ey] = center_element;
5925     }
5926
5927     // now "center_element" is finally determined -- set related values now
5928     artwork_element = center_element;           // for custom player artwork
5929     explosion_element = center_element;         // for custom player artwork
5930
5931     if (IS_PLAYER(ex, ey))
5932     {
5933       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5934
5935       artwork_element = stored_player[player_nr].artwork_element;
5936
5937       if (level.use_explosion_element[player_nr])
5938       {
5939         explosion_element = level.explosion_element[player_nr];
5940         artwork_element = explosion_element;
5941       }
5942     }
5943
5944     if (mode == EX_TYPE_NORMAL ||
5945         mode == EX_TYPE_CENTER ||
5946         mode == EX_TYPE_CROSS)
5947       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5948
5949     last_phase = element_info[explosion_element].explosion_delay + 1;
5950
5951     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5952     {
5953       int xx = x - ex + 1;
5954       int yy = y - ey + 1;
5955       int element;
5956
5957       if (!IN_LEV_FIELD(x, y) ||
5958           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5959           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5960         continue;
5961
5962       element = Tile[x][y];
5963
5964       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5965       {
5966         element = MovingOrBlocked2Element(x, y);
5967
5968         if (!IS_EXPLOSION_PROOF(element))
5969           RemoveMovingField(x, y);
5970       }
5971
5972       // indestructible elements can only explode in center (but not flames)
5973       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5974                                            mode == EX_TYPE_BORDER)) ||
5975           element == EL_FLAMES)
5976         continue;
5977
5978       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5979          behaviour, for example when touching a yamyam that explodes to rocks
5980          with active deadly shield, a rock is created under the player !!! */
5981       // (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8)
5982 #if 0
5983       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5984           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5985            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5986 #else
5987       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5988 #endif
5989       {
5990         if (IS_ACTIVE_BOMB(element))
5991         {
5992           // re-activate things under the bomb like gate or penguin
5993           Tile[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5994           Back[x][y] = 0;
5995         }
5996
5997         continue;
5998       }
5999
6000       // save walkable background elements while explosion on same tile
6001       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
6002           (x != ex || y != ey || mode == EX_TYPE_BORDER))
6003         Back[x][y] = element;
6004
6005       // ignite explodable elements reached by other explosion
6006       if (element == EL_EXPLOSION)
6007         element = Store2[x][y];
6008
6009       if (AmoebaNr[x][y] &&
6010           (element == EL_AMOEBA_FULL ||
6011            element == EL_BD_AMOEBA ||
6012            element == EL_AMOEBA_GROWING))
6013       {
6014         AmoebaCnt[AmoebaNr[x][y]]--;
6015         AmoebaCnt2[AmoebaNr[x][y]]--;
6016       }
6017
6018       RemoveField(x, y);
6019
6020       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
6021       {
6022         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
6023
6024         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
6025
6026         if (PLAYERINFO(ex, ey)->use_murphy)
6027           Store[x][y] = EL_EMPTY;
6028       }
6029
6030       // !!! check this case -- currently needed for rnd_rado_negundo_v,
6031       // !!! levels 015 018 019 020 021 022 023 026 027 028 !!!
6032       else if (IS_PLAYER_ELEMENT(center_element))
6033         Store[x][y] = EL_EMPTY;
6034       else if (center_element == EL_YAMYAM)
6035         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
6036       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
6037         Store[x][y] = element_info[center_element].content.e[xx][yy];
6038 #if 1
6039       // needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
6040       // (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
6041       // otherwise) -- FIX THIS !!!
6042       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
6043         Store[x][y] = element_info[element].content.e[1][1];
6044 #else
6045       else if (!CAN_EXPLODE(element))
6046         Store[x][y] = element_info[element].content.e[1][1];
6047 #endif
6048       else
6049         Store[x][y] = EL_EMPTY;
6050
6051       if (IS_CUSTOM_ELEMENT(center_element))
6052         Store[x][y] = (Store[x][y] == EL_CURRENT_CE_VALUE ? ce_value :
6053                        Store[x][y] == EL_CURRENT_CE_SCORE ? ce_score :
6054                        Store[x][y] >= EL_PREV_CE_8 &&
6055                        Store[x][y] <= EL_NEXT_CE_8 ?
6056                        RESOLVED_REFERENCE_ELEMENT(center_element, Store[x][y]) :
6057                        Store[x][y]);
6058
6059       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
6060           center_element == EL_AMOEBA_TO_DIAMOND)
6061         Store2[x][y] = element;
6062
6063       Tile[x][y] = EL_EXPLOSION;
6064       GfxElement[x][y] = artwork_element;
6065
6066       ExplodePhase[x][y] = 1;
6067       ExplodeDelay[x][y] = last_phase;
6068
6069       Stop[x][y] = TRUE;
6070     }
6071
6072     if (center_element == EL_YAMYAM)
6073       game.yamyam_content_nr =
6074         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
6075
6076     return;
6077   }
6078
6079   if (Stop[ex][ey])
6080     return;
6081
6082   x = ex;
6083   y = ey;
6084
6085   if (phase == 1)
6086     GfxFrame[x][y] = 0;         // restart explosion animation
6087
6088   last_phase = ExplodeDelay[x][y];
6089
6090   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
6091
6092   // this can happen if the player leaves an explosion just in time
6093   if (GfxElement[x][y] == EL_UNDEFINED)
6094     GfxElement[x][y] = EL_EMPTY;
6095
6096   border_element = Store2[x][y];
6097   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6098     border_element = StorePlayer[x][y];
6099
6100   if (phase == element_info[border_element].ignition_delay ||
6101       phase == last_phase)
6102   {
6103     boolean border_explosion = FALSE;
6104
6105     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
6106         !PLAYER_EXPLOSION_PROTECTED(x, y))
6107     {
6108       KillPlayerUnlessExplosionProtected(x, y);
6109       border_explosion = TRUE;
6110     }
6111     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
6112     {
6113       Tile[x][y] = Store2[x][y];
6114       Store2[x][y] = 0;
6115       Bang(x, y);
6116       border_explosion = TRUE;
6117     }
6118     else if (border_element == EL_AMOEBA_TO_DIAMOND)
6119     {
6120       AmoebaToDiamond(x, y);
6121       Store2[x][y] = 0;
6122       border_explosion = TRUE;
6123     }
6124
6125     // if an element just explodes due to another explosion (chain-reaction),
6126     // do not immediately end the new explosion when it was the last frame of
6127     // the explosion (as it would be done in the following "if"-statement!)
6128     if (border_explosion && phase == last_phase)
6129       return;
6130   }
6131
6132   // this can happen if the player was just killed by an explosion
6133   if (GfxElement[x][y] == EL_UNDEFINED)
6134     GfxElement[x][y] = EL_EMPTY;
6135
6136   if (phase == last_phase)
6137   {
6138     int element;
6139
6140     element = Tile[x][y] = Store[x][y];
6141     Store[x][y] = Store2[x][y] = 0;
6142     GfxElement[x][y] = EL_UNDEFINED;
6143
6144     // player can escape from explosions and might therefore be still alive
6145     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
6146         element <= EL_PLAYER_IS_EXPLODING_4)
6147     {
6148       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
6149       int explosion_element = EL_PLAYER_1 + player_nr;
6150       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
6151       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
6152
6153       if (level.use_explosion_element[player_nr])
6154         explosion_element = level.explosion_element[player_nr];
6155
6156       Tile[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
6157                     element_info[explosion_element].content.e[xx][yy]);
6158     }
6159
6160     // restore probably existing indestructible background element
6161     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
6162       element = Tile[x][y] = Back[x][y];
6163     Back[x][y] = 0;
6164
6165     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
6166     GfxDir[x][y] = MV_NONE;
6167     ChangeDelay[x][y] = 0;
6168     ChangePage[x][y] = -1;
6169
6170     CustomValue[x][y] = 0;
6171
6172     InitField_WithBug2(x, y, FALSE);
6173
6174     TEST_DrawLevelField(x, y);
6175
6176     TestIfElementTouchesCustomElement(x, y);
6177
6178     if (GFX_CRUMBLED(element))
6179       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6180
6181     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
6182       StorePlayer[x][y] = 0;
6183
6184     if (IS_PLAYER_ELEMENT(element))
6185       RelocatePlayer(x, y, element);
6186   }
6187   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6188   {
6189     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
6190     int frame = getGraphicAnimationFrameXY(graphic, x, y);
6191
6192     if (phase == 1)
6193       TEST_DrawLevelFieldCrumbled(x, y);
6194
6195     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
6196     {
6197       DrawLevelElement(x, y, Back[x][y]);
6198       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
6199     }
6200     else if (IS_WALKABLE_UNDER(Back[x][y]))
6201     {
6202       DrawLevelGraphic(x, y, graphic, frame);
6203       DrawLevelElementThruMask(x, y, Back[x][y]);
6204     }
6205     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
6206       DrawLevelGraphic(x, y, graphic, frame);
6207   }
6208 }
6209
6210 static void DynaExplode(int ex, int ey)
6211 {
6212   int i, j;
6213   int dynabomb_element = Tile[ex][ey];
6214   int dynabomb_size = 1;
6215   boolean dynabomb_xl = FALSE;
6216   struct PlayerInfo *player;
6217   struct XY *xy = xy_topdown;
6218
6219   if (IS_ACTIVE_BOMB(dynabomb_element))
6220   {
6221     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
6222     dynabomb_size = player->dynabomb_size;
6223     dynabomb_xl = player->dynabomb_xl;
6224     player->dynabombs_left++;
6225   }
6226
6227   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6228
6229   for (i = 0; i < NUM_DIRECTIONS; i++)
6230   {
6231     for (j = 1; j <= dynabomb_size; j++)
6232     {
6233       int x = ex + j * xy[i].x;
6234       int y = ey + j * xy[i].y;
6235       int element;
6236
6237       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Tile[x][y]))
6238         break;
6239
6240       element = Tile[x][y];
6241
6242       // do not restart explosions of fields with active bombs
6243       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6244         continue;
6245
6246       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6247
6248       if (element != EL_EMPTY && element != EL_EXPLOSION &&
6249           !IS_DIGGABLE(element) && !dynabomb_xl)
6250         break;
6251     }
6252   }
6253 }
6254
6255 void Bang(int x, int y)
6256 {
6257   int element = MovingOrBlocked2Element(x, y);
6258   int explosion_type = EX_TYPE_NORMAL;
6259
6260   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6261   {
6262     struct PlayerInfo *player = PLAYERINFO(x, y);
6263
6264     element = Tile[x][y] = player->initial_element;
6265
6266     if (level.use_explosion_element[player->index_nr])
6267     {
6268       int explosion_element = level.explosion_element[player->index_nr];
6269
6270       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6271         explosion_type = EX_TYPE_CROSS;
6272       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6273         explosion_type = EX_TYPE_CENTER;
6274     }
6275   }
6276
6277   switch (element)
6278   {
6279     case EL_BUG:
6280     case EL_SPACESHIP:
6281     case EL_BD_BUTTERFLY:
6282     case EL_BD_FIREFLY:
6283     case EL_YAMYAM:
6284     case EL_DARK_YAMYAM:
6285     case EL_ROBOT:
6286     case EL_PACMAN:
6287     case EL_MOLE:
6288       RaiseScoreElement(element);
6289       break;
6290
6291     case EL_DYNABOMB_PLAYER_1_ACTIVE:
6292     case EL_DYNABOMB_PLAYER_2_ACTIVE:
6293     case EL_DYNABOMB_PLAYER_3_ACTIVE:
6294     case EL_DYNABOMB_PLAYER_4_ACTIVE:
6295     case EL_DYNABOMB_INCREASE_NUMBER:
6296     case EL_DYNABOMB_INCREASE_SIZE:
6297     case EL_DYNABOMB_INCREASE_POWER:
6298       explosion_type = EX_TYPE_DYNA;
6299       break;
6300
6301     case EL_DC_LANDMINE:
6302       explosion_type = EX_TYPE_CENTER;
6303       break;
6304
6305     case EL_PENGUIN:
6306     case EL_LAMP:
6307     case EL_LAMP_ACTIVE:
6308     case EL_AMOEBA_TO_DIAMOND:
6309       if (!IS_PLAYER(x, y))     // penguin and player may be at same field
6310         explosion_type = EX_TYPE_CENTER;
6311       break;
6312
6313     default:
6314       if (element_info[element].explosion_type == EXPLODES_CROSS)
6315         explosion_type = EX_TYPE_CROSS;
6316       else if (element_info[element].explosion_type == EXPLODES_1X1)
6317         explosion_type = EX_TYPE_CENTER;
6318       break;
6319   }
6320
6321   if (explosion_type == EX_TYPE_DYNA)
6322     DynaExplode(x, y);
6323   else
6324     Explode(x, y, EX_PHASE_START, explosion_type);
6325
6326   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6327 }
6328
6329 static void SplashAcid(int x, int y)
6330 {
6331   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6332       (!IN_LEV_FIELD(x - 1, y - 2) ||
6333        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6334     Tile[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6335
6336   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6337       (!IN_LEV_FIELD(x + 1, y - 2) ||
6338        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6339     Tile[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6340
6341   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6342 }
6343
6344 static void InitBeltMovement(void)
6345 {
6346   static int belt_base_element[4] =
6347   {
6348     EL_CONVEYOR_BELT_1_LEFT,
6349     EL_CONVEYOR_BELT_2_LEFT,
6350     EL_CONVEYOR_BELT_3_LEFT,
6351     EL_CONVEYOR_BELT_4_LEFT
6352   };
6353   static int belt_base_active_element[4] =
6354   {
6355     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6356     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6357     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6358     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6359   };
6360
6361   int x, y, i, j;
6362
6363   // set frame order for belt animation graphic according to belt direction
6364   for (i = 0; i < NUM_BELTS; i++)
6365   {
6366     int belt_nr = i;
6367
6368     for (j = 0; j < NUM_BELT_PARTS; j++)
6369     {
6370       int element = belt_base_active_element[belt_nr] + j;
6371       int graphic_1 = el2img(element);
6372       int graphic_2 = el2panelimg(element);
6373
6374       if (game.belt_dir[i] == MV_LEFT)
6375       {
6376         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6377         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6378       }
6379       else
6380       {
6381         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6382         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6383       }
6384     }
6385   }
6386
6387   SCAN_PLAYFIELD(x, y)
6388   {
6389     int element = Tile[x][y];
6390
6391     for (i = 0; i < NUM_BELTS; i++)
6392     {
6393       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6394       {
6395         int e_belt_nr = getBeltNrFromBeltElement(element);
6396         int belt_nr = i;
6397
6398         if (e_belt_nr == belt_nr)
6399         {
6400           int belt_part = Tile[x][y] - belt_base_element[belt_nr];
6401
6402           Tile[x][y] = belt_base_active_element[belt_nr] + belt_part;
6403         }
6404       }
6405     }
6406   }
6407 }
6408
6409 static void ToggleBeltSwitch(int x, int y)
6410 {
6411   static int belt_base_element[4] =
6412   {
6413     EL_CONVEYOR_BELT_1_LEFT,
6414     EL_CONVEYOR_BELT_2_LEFT,
6415     EL_CONVEYOR_BELT_3_LEFT,
6416     EL_CONVEYOR_BELT_4_LEFT
6417   };
6418   static int belt_base_active_element[4] =
6419   {
6420     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6421     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6422     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6423     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6424   };
6425   static int belt_base_switch_element[4] =
6426   {
6427     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6428     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6429     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6430     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6431   };
6432   static int belt_move_dir[4] =
6433   {
6434     MV_LEFT,
6435     MV_NONE,
6436     MV_RIGHT,
6437     MV_NONE,
6438   };
6439
6440   int element = Tile[x][y];
6441   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6442   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6443   int belt_dir = belt_move_dir[belt_dir_nr];
6444   int xx, yy, i;
6445
6446   if (!IS_BELT_SWITCH(element))
6447     return;
6448
6449   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6450   game.belt_dir[belt_nr] = belt_dir;
6451
6452   if (belt_dir_nr == 3)
6453     belt_dir_nr = 1;
6454
6455   // set frame order for belt animation graphic according to belt direction
6456   for (i = 0; i < NUM_BELT_PARTS; i++)
6457   {
6458     int element = belt_base_active_element[belt_nr] + i;
6459     int graphic_1 = el2img(element);
6460     int graphic_2 = el2panelimg(element);
6461
6462     if (belt_dir == MV_LEFT)
6463     {
6464       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6465       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6466     }
6467     else
6468     {
6469       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6470       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6471     }
6472   }
6473
6474   SCAN_PLAYFIELD(xx, yy)
6475   {
6476     int element = Tile[xx][yy];
6477
6478     if (IS_BELT_SWITCH(element))
6479     {
6480       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6481
6482       if (e_belt_nr == belt_nr)
6483       {
6484         Tile[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6485         TEST_DrawLevelField(xx, yy);
6486       }
6487     }
6488     else if (IS_BELT(element) && belt_dir != MV_NONE)
6489     {
6490       int e_belt_nr = getBeltNrFromBeltElement(element);
6491
6492       if (e_belt_nr == belt_nr)
6493       {
6494         int belt_part = Tile[xx][yy] - belt_base_element[belt_nr];
6495
6496         Tile[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6497         TEST_DrawLevelField(xx, yy);
6498       }
6499     }
6500     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6501     {
6502       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6503
6504       if (e_belt_nr == belt_nr)
6505       {
6506         int belt_part = Tile[xx][yy] - belt_base_active_element[belt_nr];
6507
6508         Tile[xx][yy] = belt_base_element[belt_nr] + belt_part;
6509         TEST_DrawLevelField(xx, yy);
6510       }
6511     }
6512   }
6513 }
6514
6515 static void ToggleSwitchgateSwitch(void)
6516 {
6517   int xx, yy;
6518
6519   game.switchgate_pos = !game.switchgate_pos;
6520
6521   SCAN_PLAYFIELD(xx, yy)
6522   {
6523     int element = Tile[xx][yy];
6524
6525     if (element == EL_SWITCHGATE_SWITCH_UP)
6526     {
6527       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6528       TEST_DrawLevelField(xx, yy);
6529     }
6530     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6531     {
6532       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6533       TEST_DrawLevelField(xx, yy);
6534     }
6535     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6536     {
6537       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6538       TEST_DrawLevelField(xx, yy);
6539     }
6540     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6541     {
6542       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6543       TEST_DrawLevelField(xx, yy);
6544     }
6545     else if (element == EL_SWITCHGATE_OPEN ||
6546              element == EL_SWITCHGATE_OPENING)
6547     {
6548       Tile[xx][yy] = EL_SWITCHGATE_CLOSING;
6549
6550       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6551     }
6552     else if (element == EL_SWITCHGATE_CLOSED ||
6553              element == EL_SWITCHGATE_CLOSING)
6554     {
6555       Tile[xx][yy] = EL_SWITCHGATE_OPENING;
6556
6557       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6558     }
6559   }
6560 }
6561
6562 static int getInvisibleActiveFromInvisibleElement(int element)
6563 {
6564   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6565           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6566           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6567           element);
6568 }
6569
6570 static int getInvisibleFromInvisibleActiveElement(int element)
6571 {
6572   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6573           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6574           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6575           element);
6576 }
6577
6578 static void RedrawAllLightSwitchesAndInvisibleElements(void)
6579 {
6580   int x, y;
6581
6582   SCAN_PLAYFIELD(x, y)
6583   {
6584     int element = Tile[x][y];
6585
6586     if (element == EL_LIGHT_SWITCH &&
6587         game.light_time_left > 0)
6588     {
6589       Tile[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6590       TEST_DrawLevelField(x, y);
6591     }
6592     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6593              game.light_time_left == 0)
6594     {
6595       Tile[x][y] = EL_LIGHT_SWITCH;
6596       TEST_DrawLevelField(x, y);
6597     }
6598     else if (element == EL_EMC_DRIPPER &&
6599              game.light_time_left > 0)
6600     {
6601       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6602       TEST_DrawLevelField(x, y);
6603     }
6604     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6605              game.light_time_left == 0)
6606     {
6607       Tile[x][y] = EL_EMC_DRIPPER;
6608       TEST_DrawLevelField(x, y);
6609     }
6610     else if (element == EL_INVISIBLE_STEELWALL ||
6611              element == EL_INVISIBLE_WALL ||
6612              element == EL_INVISIBLE_SAND)
6613     {
6614       if (game.light_time_left > 0)
6615         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6616
6617       TEST_DrawLevelField(x, y);
6618
6619       // uncrumble neighbour fields, if needed
6620       if (element == EL_INVISIBLE_SAND)
6621         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6622     }
6623     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6624              element == EL_INVISIBLE_WALL_ACTIVE ||
6625              element == EL_INVISIBLE_SAND_ACTIVE)
6626     {
6627       if (game.light_time_left == 0)
6628         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6629
6630       TEST_DrawLevelField(x, y);
6631
6632       // re-crumble neighbour fields, if needed
6633       if (element == EL_INVISIBLE_SAND)
6634         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6635     }
6636   }
6637 }
6638
6639 static void RedrawAllInvisibleElementsForLenses(void)
6640 {
6641   int x, y;
6642
6643   SCAN_PLAYFIELD(x, y)
6644   {
6645     int element = Tile[x][y];
6646
6647     if (element == EL_EMC_DRIPPER &&
6648         game.lenses_time_left > 0)
6649     {
6650       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6651       TEST_DrawLevelField(x, y);
6652     }
6653     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6654              game.lenses_time_left == 0)
6655     {
6656       Tile[x][y] = EL_EMC_DRIPPER;
6657       TEST_DrawLevelField(x, y);
6658     }
6659     else if (element == EL_INVISIBLE_STEELWALL ||
6660              element == EL_INVISIBLE_WALL ||
6661              element == EL_INVISIBLE_SAND)
6662     {
6663       if (game.lenses_time_left > 0)
6664         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6665
6666       TEST_DrawLevelField(x, y);
6667
6668       // uncrumble neighbour fields, if needed
6669       if (element == EL_INVISIBLE_SAND)
6670         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6671     }
6672     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6673              element == EL_INVISIBLE_WALL_ACTIVE ||
6674              element == EL_INVISIBLE_SAND_ACTIVE)
6675     {
6676       if (game.lenses_time_left == 0)
6677         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6678
6679       TEST_DrawLevelField(x, y);
6680
6681       // re-crumble neighbour fields, if needed
6682       if (element == EL_INVISIBLE_SAND)
6683         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6684     }
6685   }
6686 }
6687
6688 static void RedrawAllInvisibleElementsForMagnifier(void)
6689 {
6690   int x, y;
6691
6692   SCAN_PLAYFIELD(x, y)
6693   {
6694     int element = Tile[x][y];
6695
6696     if (element == EL_EMC_FAKE_GRASS &&
6697         game.magnify_time_left > 0)
6698     {
6699       Tile[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6700       TEST_DrawLevelField(x, y);
6701     }
6702     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6703              game.magnify_time_left == 0)
6704     {
6705       Tile[x][y] = EL_EMC_FAKE_GRASS;
6706       TEST_DrawLevelField(x, y);
6707     }
6708     else if (IS_GATE_GRAY(element) &&
6709              game.magnify_time_left > 0)
6710     {
6711       Tile[x][y] = (IS_RND_GATE_GRAY(element) ?
6712                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6713                     IS_EM_GATE_GRAY(element) ?
6714                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6715                     IS_EMC_GATE_GRAY(element) ?
6716                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6717                     IS_DC_GATE_GRAY(element) ?
6718                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6719                     element);
6720       TEST_DrawLevelField(x, y);
6721     }
6722     else if (IS_GATE_GRAY_ACTIVE(element) &&
6723              game.magnify_time_left == 0)
6724     {
6725       Tile[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6726                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6727                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6728                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6729                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6730                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6731                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6732                     EL_DC_GATE_WHITE_GRAY :
6733                     element);
6734       TEST_DrawLevelField(x, y);
6735     }
6736   }
6737 }
6738
6739 static void ToggleLightSwitch(int x, int y)
6740 {
6741   int element = Tile[x][y];
6742
6743   game.light_time_left =
6744     (element == EL_LIGHT_SWITCH ?
6745      level.time_light * FRAMES_PER_SECOND : 0);
6746
6747   RedrawAllLightSwitchesAndInvisibleElements();
6748 }
6749
6750 static void ActivateTimegateSwitch(int x, int y)
6751 {
6752   int xx, yy;
6753
6754   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6755
6756   SCAN_PLAYFIELD(xx, yy)
6757   {
6758     int element = Tile[xx][yy];
6759
6760     if (element == EL_TIMEGATE_CLOSED ||
6761         element == EL_TIMEGATE_CLOSING)
6762     {
6763       Tile[xx][yy] = EL_TIMEGATE_OPENING;
6764       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6765     }
6766
6767     /*
6768     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6769     {
6770       Tile[xx][yy] = EL_TIMEGATE_SWITCH;
6771       TEST_DrawLevelField(xx, yy);
6772     }
6773     */
6774
6775   }
6776
6777   Tile[x][y] = (Tile[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6778                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6779 }
6780
6781 static void Impact(int x, int y)
6782 {
6783   boolean last_line = (y == lev_fieldy - 1);
6784   boolean object_hit = FALSE;
6785   boolean impact = (last_line || object_hit);
6786   int element = Tile[x][y];
6787   int smashed = EL_STEELWALL;
6788
6789   if (!last_line)       // check if element below was hit
6790   {
6791     if (Tile[x][y + 1] == EL_PLAYER_IS_LEAVING)
6792       return;
6793
6794     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6795                                          MovDir[x][y + 1] != MV_DOWN ||
6796                                          MovPos[x][y + 1] <= TILEY / 2));
6797
6798     // do not smash moving elements that left the smashed field in time
6799     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6800         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6801       object_hit = FALSE;
6802
6803 #if USE_QUICKSAND_IMPACT_BUGFIX
6804     if (Tile[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6805     {
6806       RemoveMovingField(x, y + 1);
6807       Tile[x][y + 1] = EL_QUICKSAND_EMPTY;
6808       Tile[x][y + 2] = EL_ROCK;
6809       TEST_DrawLevelField(x, y + 2);
6810
6811       object_hit = TRUE;
6812     }
6813
6814     if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6815     {
6816       RemoveMovingField(x, y + 1);
6817       Tile[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6818       Tile[x][y + 2] = EL_ROCK;
6819       TEST_DrawLevelField(x, y + 2);
6820
6821       object_hit = TRUE;
6822     }
6823 #endif
6824
6825     if (object_hit)
6826       smashed = MovingOrBlocked2Element(x, y + 1);
6827
6828     impact = (last_line || object_hit);
6829   }
6830
6831   if (!last_line && smashed == EL_ACID) // element falls into acid
6832   {
6833     SplashAcid(x, y + 1);
6834     return;
6835   }
6836
6837   // !!! not sufficient for all cases -- see EL_PEARL below !!!
6838   // only reset graphic animation if graphic really changes after impact
6839   if (impact &&
6840       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6841   {
6842     ResetGfxAnimation(x, y);
6843     TEST_DrawLevelField(x, y);
6844   }
6845
6846   if (impact && CAN_EXPLODE_IMPACT(element))
6847   {
6848     Bang(x, y);
6849     return;
6850   }
6851   else if (impact && element == EL_PEARL &&
6852            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6853   {
6854     ResetGfxAnimation(x, y);
6855
6856     Tile[x][y] = EL_PEARL_BREAKING;
6857     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6858     return;
6859   }
6860   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6861   {
6862     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6863
6864     return;
6865   }
6866
6867   if (impact && element == EL_AMOEBA_DROP)
6868   {
6869     if (object_hit && IS_PLAYER(x, y + 1))
6870       KillPlayerUnlessEnemyProtected(x, y + 1);
6871     else if (object_hit && smashed == EL_PENGUIN)
6872       Bang(x, y + 1);
6873     else
6874     {
6875       Tile[x][y] = EL_AMOEBA_GROWING;
6876       Store[x][y] = EL_AMOEBA_WET;
6877
6878       ResetRandomAnimationValue(x, y);
6879     }
6880     return;
6881   }
6882
6883   if (object_hit)               // check which object was hit
6884   {
6885     if ((CAN_PASS_MAGIC_WALL(element) && 
6886          (smashed == EL_MAGIC_WALL ||
6887           smashed == EL_BD_MAGIC_WALL)) ||
6888         (CAN_PASS_DC_MAGIC_WALL(element) &&
6889          smashed == EL_DC_MAGIC_WALL))
6890     {
6891       int xx, yy;
6892       int activated_magic_wall =
6893         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6894          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6895          EL_DC_MAGIC_WALL_ACTIVE);
6896
6897       // activate magic wall / mill
6898       SCAN_PLAYFIELD(xx, yy)
6899       {
6900         if (Tile[xx][yy] == smashed)
6901           Tile[xx][yy] = activated_magic_wall;
6902       }
6903
6904       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6905       game.magic_wall_active = TRUE;
6906
6907       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6908                             SND_MAGIC_WALL_ACTIVATING :
6909                             smashed == EL_BD_MAGIC_WALL ?
6910                             SND_BD_MAGIC_WALL_ACTIVATING :
6911                             SND_DC_MAGIC_WALL_ACTIVATING));
6912     }
6913
6914     if (IS_PLAYER(x, y + 1))
6915     {
6916       if (CAN_SMASH_PLAYER(element))
6917       {
6918         KillPlayerUnlessEnemyProtected(x, y + 1);
6919         return;
6920       }
6921     }
6922     else if (smashed == EL_PENGUIN)
6923     {
6924       if (CAN_SMASH_PLAYER(element))
6925       {
6926         Bang(x, y + 1);
6927         return;
6928       }
6929     }
6930     else if (element == EL_BD_DIAMOND)
6931     {
6932       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6933       {
6934         Bang(x, y + 1);
6935         return;
6936       }
6937     }
6938     else if (((element == EL_SP_INFOTRON ||
6939                element == EL_SP_ZONK) &&
6940               (smashed == EL_SP_SNIKSNAK ||
6941                smashed == EL_SP_ELECTRON ||
6942                smashed == EL_SP_DISK_ORANGE)) ||
6943              (element == EL_SP_INFOTRON &&
6944               smashed == EL_SP_DISK_YELLOW))
6945     {
6946       Bang(x, y + 1);
6947       return;
6948     }
6949     else if (CAN_SMASH_EVERYTHING(element))
6950     {
6951       if (IS_CLASSIC_ENEMY(smashed) ||
6952           CAN_EXPLODE_SMASHED(smashed))
6953       {
6954         Bang(x, y + 1);
6955         return;
6956       }
6957       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6958       {
6959         if (smashed == EL_LAMP ||
6960             smashed == EL_LAMP_ACTIVE)
6961         {
6962           Bang(x, y + 1);
6963           return;
6964         }
6965         else if (smashed == EL_NUT)
6966         {
6967           Tile[x][y + 1] = EL_NUT_BREAKING;
6968           PlayLevelSound(x, y, SND_NUT_BREAKING);
6969           RaiseScoreElement(EL_NUT);
6970           return;
6971         }
6972         else if (smashed == EL_PEARL)
6973         {
6974           ResetGfxAnimation(x, y);
6975
6976           Tile[x][y + 1] = EL_PEARL_BREAKING;
6977           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6978           return;
6979         }
6980         else if (smashed == EL_DIAMOND)
6981         {
6982           Tile[x][y + 1] = EL_DIAMOND_BREAKING;
6983           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6984           return;
6985         }
6986         else if (IS_BELT_SWITCH(smashed))
6987         {
6988           ToggleBeltSwitch(x, y + 1);
6989         }
6990         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6991                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6992                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6993                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6994         {
6995           ToggleSwitchgateSwitch();
6996         }
6997         else if (smashed == EL_LIGHT_SWITCH ||
6998                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6999         {
7000           ToggleLightSwitch(x, y + 1);
7001         }
7002         else
7003         {
7004           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
7005
7006           CheckElementChangeBySide(x, y + 1, smashed, element,
7007                                    CE_SWITCHED, CH_SIDE_TOP);
7008           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
7009                                             CH_SIDE_TOP);
7010         }
7011       }
7012       else
7013       {
7014         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
7015       }
7016     }
7017   }
7018
7019   // play sound of magic wall / mill
7020   if (!last_line &&
7021       (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7022        Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
7023        Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
7024   {
7025     if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7026       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
7027     else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7028       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
7029     else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7030       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
7031
7032     return;
7033   }
7034
7035   // play sound of object that hits the ground
7036   if (last_line || object_hit)
7037     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
7038 }
7039
7040 static void TurnRoundExt(int x, int y)
7041 {
7042   static struct
7043   {
7044     int dx, dy;
7045   } move_xy[] =
7046   {
7047     {  0,  0 },
7048     { -1,  0 },
7049     { +1,  0 },
7050     {  0,  0 },
7051     {  0, -1 },
7052     {  0,  0 }, { 0, 0 }, { 0, 0 },
7053     {  0, +1 }
7054   };
7055   static struct
7056   {
7057     int left, right, back;
7058   } turn[] =
7059   {
7060     { 0,        0,              0        },
7061     { MV_DOWN,  MV_UP,          MV_RIGHT },
7062     { MV_UP,    MV_DOWN,        MV_LEFT  },
7063     { 0,        0,              0        },
7064     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
7065     { 0,        0,              0        },
7066     { 0,        0,              0        },
7067     { 0,        0,              0        },
7068     { MV_RIGHT, MV_LEFT,        MV_UP    }
7069   };
7070
7071   int element = Tile[x][y];
7072   int move_pattern = element_info[element].move_pattern;
7073
7074   int old_move_dir = MovDir[x][y];
7075   int left_dir  = turn[old_move_dir].left;
7076   int right_dir = turn[old_move_dir].right;
7077   int back_dir  = turn[old_move_dir].back;
7078
7079   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
7080   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
7081   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
7082   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
7083
7084   int left_x  = x + left_dx,  left_y  = y + left_dy;
7085   int right_x = x + right_dx, right_y = y + right_dy;
7086   int move_x  = x + move_dx,  move_y  = y + move_dy;
7087
7088   int xx, yy;
7089
7090   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
7091   {
7092     TestIfBadThingTouchesOtherBadThing(x, y);
7093
7094     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
7095       MovDir[x][y] = right_dir;
7096     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7097       MovDir[x][y] = left_dir;
7098
7099     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
7100       MovDelay[x][y] = 9;
7101     else if (element == EL_BD_BUTTERFLY)     // && MovDir[x][y] == left_dir)
7102       MovDelay[x][y] = 1;
7103   }
7104   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
7105   {
7106     TestIfBadThingTouchesOtherBadThing(x, y);
7107
7108     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
7109       MovDir[x][y] = left_dir;
7110     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7111       MovDir[x][y] = right_dir;
7112
7113     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
7114       MovDelay[x][y] = 9;
7115     else if (element == EL_BD_FIREFLY)      // && MovDir[x][y] == right_dir)
7116       MovDelay[x][y] = 1;
7117   }
7118   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
7119   {
7120     TestIfBadThingTouchesOtherBadThing(x, y);
7121
7122     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
7123       MovDir[x][y] = left_dir;
7124     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
7125       MovDir[x][y] = right_dir;
7126
7127     if (MovDir[x][y] != old_move_dir)
7128       MovDelay[x][y] = 9;
7129   }
7130   else if (element == EL_YAMYAM)
7131   {
7132     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
7133     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
7134
7135     if (can_turn_left && can_turn_right)
7136       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7137     else if (can_turn_left)
7138       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7139     else if (can_turn_right)
7140       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7141     else
7142       MovDir[x][y] = back_dir;
7143
7144     MovDelay[x][y] = 16 + 16 * RND(3);
7145   }
7146   else if (element == EL_DARK_YAMYAM)
7147   {
7148     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7149                                                          left_x, left_y);
7150     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7151                                                          right_x, right_y);
7152
7153     if (can_turn_left && can_turn_right)
7154       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7155     else if (can_turn_left)
7156       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7157     else if (can_turn_right)
7158       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7159     else
7160       MovDir[x][y] = back_dir;
7161
7162     MovDelay[x][y] = 16 + 16 * RND(3);
7163   }
7164   else if (element == EL_PACMAN)
7165   {
7166     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
7167     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
7168
7169     if (can_turn_left && can_turn_right)
7170       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7171     else if (can_turn_left)
7172       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7173     else if (can_turn_right)
7174       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7175     else
7176       MovDir[x][y] = back_dir;
7177
7178     MovDelay[x][y] = 6 + RND(40);
7179   }
7180   else if (element == EL_PIG)
7181   {
7182     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
7183     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
7184     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
7185     boolean should_turn_left, should_turn_right, should_move_on;
7186     int rnd_value = 24;
7187     int rnd = RND(rnd_value);
7188
7189     should_turn_left = (can_turn_left &&
7190                         (!can_move_on ||
7191                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
7192                                                    y + back_dy + left_dy)));
7193     should_turn_right = (can_turn_right &&
7194                          (!can_move_on ||
7195                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
7196                                                     y + back_dy + right_dy)));
7197     should_move_on = (can_move_on &&
7198                       (!can_turn_left ||
7199                        !can_turn_right ||
7200                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
7201                                                  y + move_dy + left_dy) ||
7202                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
7203                                                  y + move_dy + right_dy)));
7204
7205     if (should_turn_left || should_turn_right || should_move_on)
7206     {
7207       if (should_turn_left && should_turn_right && should_move_on)
7208         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
7209                         rnd < 2 * rnd_value / 3 ? right_dir :
7210                         old_move_dir);
7211       else if (should_turn_left && should_turn_right)
7212         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7213       else if (should_turn_left && should_move_on)
7214         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
7215       else if (should_turn_right && should_move_on)
7216         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
7217       else if (should_turn_left)
7218         MovDir[x][y] = left_dir;
7219       else if (should_turn_right)
7220         MovDir[x][y] = right_dir;
7221       else if (should_move_on)
7222         MovDir[x][y] = old_move_dir;
7223     }
7224     else if (can_move_on && rnd > rnd_value / 8)
7225       MovDir[x][y] = old_move_dir;
7226     else if (can_turn_left && can_turn_right)
7227       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7228     else if (can_turn_left && rnd > rnd_value / 8)
7229       MovDir[x][y] = left_dir;
7230     else if (can_turn_right && rnd > rnd_value/8)
7231       MovDir[x][y] = right_dir;
7232     else
7233       MovDir[x][y] = back_dir;
7234
7235     xx = x + move_xy[MovDir[x][y]].dx;
7236     yy = y + move_xy[MovDir[x][y]].dy;
7237
7238     if (!IN_LEV_FIELD(xx, yy) ||
7239         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Tile[xx][yy])))
7240       MovDir[x][y] = old_move_dir;
7241
7242     MovDelay[x][y] = 0;
7243   }
7244   else if (element == EL_DRAGON)
7245   {
7246     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7247     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7248     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7249     int rnd_value = 24;
7250     int rnd = RND(rnd_value);
7251
7252     if (can_move_on && rnd > rnd_value / 8)
7253       MovDir[x][y] = old_move_dir;
7254     else if (can_turn_left && can_turn_right)
7255       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7256     else if (can_turn_left && rnd > rnd_value / 8)
7257       MovDir[x][y] = left_dir;
7258     else if (can_turn_right && rnd > rnd_value / 8)
7259       MovDir[x][y] = right_dir;
7260     else
7261       MovDir[x][y] = back_dir;
7262
7263     xx = x + move_xy[MovDir[x][y]].dx;
7264     yy = y + move_xy[MovDir[x][y]].dy;
7265
7266     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7267       MovDir[x][y] = old_move_dir;
7268
7269     MovDelay[x][y] = 0;
7270   }
7271   else if (element == EL_MOLE)
7272   {
7273     boolean can_move_on =
7274       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7275                             IS_AMOEBOID(Tile[move_x][move_y]) ||
7276                             Tile[move_x][move_y] == EL_AMOEBA_SHRINKING));
7277     if (!can_move_on)
7278     {
7279       boolean can_turn_left =
7280         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7281                               IS_AMOEBOID(Tile[left_x][left_y])));
7282
7283       boolean can_turn_right =
7284         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7285                               IS_AMOEBOID(Tile[right_x][right_y])));
7286
7287       if (can_turn_left && can_turn_right)
7288         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7289       else if (can_turn_left)
7290         MovDir[x][y] = left_dir;
7291       else
7292         MovDir[x][y] = right_dir;
7293     }
7294
7295     if (MovDir[x][y] != old_move_dir)
7296       MovDelay[x][y] = 9;
7297   }
7298   else if (element == EL_BALLOON)
7299   {
7300     MovDir[x][y] = game.wind_direction;
7301     MovDelay[x][y] = 0;
7302   }
7303   else if (element == EL_SPRING)
7304   {
7305     if (MovDir[x][y] & MV_HORIZONTAL)
7306     {
7307       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7308           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7309       {
7310         Tile[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7311         ResetGfxAnimation(move_x, move_y);
7312         TEST_DrawLevelField(move_x, move_y);
7313
7314         MovDir[x][y] = back_dir;
7315       }
7316       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7317                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7318         MovDir[x][y] = MV_NONE;
7319     }
7320
7321     MovDelay[x][y] = 0;
7322   }
7323   else if (element == EL_ROBOT ||
7324            element == EL_SATELLITE ||
7325            element == EL_PENGUIN ||
7326            element == EL_EMC_ANDROID)
7327   {
7328     int attr_x = -1, attr_y = -1;
7329
7330     if (game.all_players_gone)
7331     {
7332       attr_x = game.exit_x;
7333       attr_y = game.exit_y;
7334     }
7335     else
7336     {
7337       int i;
7338
7339       for (i = 0; i < MAX_PLAYERS; i++)
7340       {
7341         struct PlayerInfo *player = &stored_player[i];
7342         int jx = player->jx, jy = player->jy;
7343
7344         if (!player->active)
7345           continue;
7346
7347         if (attr_x == -1 ||
7348             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7349         {
7350           attr_x = jx;
7351           attr_y = jy;
7352         }
7353       }
7354     }
7355
7356     if (element == EL_ROBOT &&
7357         game.robot_wheel_x >= 0 &&
7358         game.robot_wheel_y >= 0 &&
7359         (Tile[game.robot_wheel_x][game.robot_wheel_y] == EL_ROBOT_WHEEL_ACTIVE ||
7360          game.engine_version < VERSION_IDENT(3,1,0,0)))
7361     {
7362       attr_x = game.robot_wheel_x;
7363       attr_y = game.robot_wheel_y;
7364     }
7365
7366     if (element == EL_PENGUIN)
7367     {
7368       int i;
7369       struct XY *xy = xy_topdown;
7370
7371       for (i = 0; i < NUM_DIRECTIONS; i++)
7372       {
7373         int ex = x + xy[i].x;
7374         int ey = y + xy[i].y;
7375
7376         if (IN_LEV_FIELD(ex, ey) && (Tile[ex][ey] == EL_EXIT_OPEN ||
7377                                      Tile[ex][ey] == EL_EM_EXIT_OPEN ||
7378                                      Tile[ex][ey] == EL_STEEL_EXIT_OPEN ||
7379                                      Tile[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7380         {
7381           attr_x = ex;
7382           attr_y = ey;
7383           break;
7384         }
7385       }
7386     }
7387
7388     MovDir[x][y] = MV_NONE;
7389     if (attr_x < x)
7390       MovDir[x][y] |= (game.all_players_gone ? MV_RIGHT : MV_LEFT);
7391     else if (attr_x > x)
7392       MovDir[x][y] |= (game.all_players_gone ? MV_LEFT : MV_RIGHT);
7393     if (attr_y < y)
7394       MovDir[x][y] |= (game.all_players_gone ? MV_DOWN : MV_UP);
7395     else if (attr_y > y)
7396       MovDir[x][y] |= (game.all_players_gone ? MV_UP : MV_DOWN);
7397
7398     if (element == EL_ROBOT)
7399     {
7400       int newx, newy;
7401
7402       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7403         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7404       Moving2Blocked(x, y, &newx, &newy);
7405
7406       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7407         MovDelay[x][y] = 8 + 8 * !RND(3);
7408       else
7409         MovDelay[x][y] = 16;
7410     }
7411     else if (element == EL_PENGUIN)
7412     {
7413       int newx, newy;
7414
7415       MovDelay[x][y] = 1;
7416
7417       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7418       {
7419         boolean first_horiz = RND(2);
7420         int new_move_dir = MovDir[x][y];
7421
7422         MovDir[x][y] =
7423           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7424         Moving2Blocked(x, y, &newx, &newy);
7425
7426         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7427           return;
7428
7429         MovDir[x][y] =
7430           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7431         Moving2Blocked(x, y, &newx, &newy);
7432
7433         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7434           return;
7435
7436         MovDir[x][y] = old_move_dir;
7437         return;
7438       }
7439     }
7440     else if (element == EL_SATELLITE)
7441     {
7442       int newx, newy;
7443
7444       MovDelay[x][y] = 1;
7445
7446       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7447       {
7448         boolean first_horiz = RND(2);
7449         int new_move_dir = MovDir[x][y];
7450
7451         MovDir[x][y] =
7452           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7453         Moving2Blocked(x, y, &newx, &newy);
7454
7455         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7456           return;
7457
7458         MovDir[x][y] =
7459           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7460         Moving2Blocked(x, y, &newx, &newy);
7461
7462         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7463           return;
7464
7465         MovDir[x][y] = old_move_dir;
7466         return;
7467       }
7468     }
7469     else if (element == EL_EMC_ANDROID)
7470     {
7471       static int check_pos[16] =
7472       {
7473         -1,             //  0 => (invalid)
7474         7,              //  1 => MV_LEFT
7475         3,              //  2 => MV_RIGHT
7476         -1,             //  3 => (invalid)
7477         1,              //  4 =>            MV_UP
7478         0,              //  5 => MV_LEFT  | MV_UP
7479         2,              //  6 => MV_RIGHT | MV_UP
7480         -1,             //  7 => (invalid)
7481         5,              //  8 =>            MV_DOWN
7482         6,              //  9 => MV_LEFT  | MV_DOWN
7483         4,              // 10 => MV_RIGHT | MV_DOWN
7484         -1,             // 11 => (invalid)
7485         -1,             // 12 => (invalid)
7486         -1,             // 13 => (invalid)
7487         -1,             // 14 => (invalid)
7488         -1,             // 15 => (invalid)
7489       };
7490       static struct
7491       {
7492         int dx, dy;
7493         int dir;
7494       } check_xy[8] =
7495       {
7496         { -1, -1,       MV_LEFT  | MV_UP   },
7497         {  0, -1,                  MV_UP   },
7498         { +1, -1,       MV_RIGHT | MV_UP   },
7499         { +1,  0,       MV_RIGHT           },
7500         { +1, +1,       MV_RIGHT | MV_DOWN },
7501         {  0, +1,                  MV_DOWN },
7502         { -1, +1,       MV_LEFT  | MV_DOWN },
7503         { -1,  0,       MV_LEFT            },
7504       };
7505       int start_pos, check_order;
7506       boolean can_clone = FALSE;
7507       int i;
7508
7509       // check if there is any free field around current position
7510       for (i = 0; i < 8; i++)
7511       {
7512         int newx = x + check_xy[i].dx;
7513         int newy = y + check_xy[i].dy;
7514
7515         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7516         {
7517           can_clone = TRUE;
7518
7519           break;
7520         }
7521       }
7522
7523       if (can_clone)            // randomly find an element to clone
7524       {
7525         can_clone = FALSE;
7526
7527         start_pos = check_pos[RND(8)];
7528         check_order = (RND(2) ? -1 : +1);
7529
7530         for (i = 0; i < 8; i++)
7531         {
7532           int pos_raw = start_pos + i * check_order;
7533           int pos = (pos_raw + 8) % 8;
7534           int newx = x + check_xy[pos].dx;
7535           int newy = y + check_xy[pos].dy;
7536
7537           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7538           {
7539             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7540             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7541
7542             Store[x][y] = Tile[newx][newy];
7543
7544             can_clone = TRUE;
7545
7546             break;
7547           }
7548         }
7549       }
7550
7551       if (can_clone)            // randomly find a direction to move
7552       {
7553         can_clone = FALSE;
7554
7555         start_pos = check_pos[RND(8)];
7556         check_order = (RND(2) ? -1 : +1);
7557
7558         for (i = 0; i < 8; i++)
7559         {
7560           int pos_raw = start_pos + i * check_order;
7561           int pos = (pos_raw + 8) % 8;
7562           int newx = x + check_xy[pos].dx;
7563           int newy = y + check_xy[pos].dy;
7564           int new_move_dir = check_xy[pos].dir;
7565
7566           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7567           {
7568             MovDir[x][y] = new_move_dir;
7569             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7570
7571             can_clone = TRUE;
7572
7573             break;
7574           }
7575         }
7576       }
7577
7578       if (can_clone)            // cloning and moving successful
7579         return;
7580
7581       // cannot clone -- try to move towards player
7582
7583       start_pos = check_pos[MovDir[x][y] & 0x0f];
7584       check_order = (RND(2) ? -1 : +1);
7585
7586       for (i = 0; i < 3; i++)
7587       {
7588         // first check start_pos, then previous/next or (next/previous) pos
7589         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7590         int pos = (pos_raw + 8) % 8;
7591         int newx = x + check_xy[pos].dx;
7592         int newy = y + check_xy[pos].dy;
7593         int new_move_dir = check_xy[pos].dir;
7594
7595         if (IS_PLAYER(newx, newy))
7596           break;
7597
7598         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7599         {
7600           MovDir[x][y] = new_move_dir;
7601           MovDelay[x][y] = level.android_move_time * 8 + 1;
7602
7603           break;
7604         }
7605       }
7606     }
7607   }
7608   else if (move_pattern == MV_TURNING_LEFT ||
7609            move_pattern == MV_TURNING_RIGHT ||
7610            move_pattern == MV_TURNING_LEFT_RIGHT ||
7611            move_pattern == MV_TURNING_RIGHT_LEFT ||
7612            move_pattern == MV_TURNING_RANDOM ||
7613            move_pattern == MV_ALL_DIRECTIONS)
7614   {
7615     boolean can_turn_left =
7616       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7617     boolean can_turn_right =
7618       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y);
7619
7620     if (element_info[element].move_stepsize == 0)       // "not moving"
7621       return;
7622
7623     if (move_pattern == MV_TURNING_LEFT)
7624       MovDir[x][y] = left_dir;
7625     else if (move_pattern == MV_TURNING_RIGHT)
7626       MovDir[x][y] = right_dir;
7627     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7628       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7629     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7630       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7631     else if (move_pattern == MV_TURNING_RANDOM)
7632       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7633                       can_turn_right && !can_turn_left ? right_dir :
7634                       RND(2) ? left_dir : right_dir);
7635     else if (can_turn_left && can_turn_right)
7636       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7637     else if (can_turn_left)
7638       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7639     else if (can_turn_right)
7640       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7641     else
7642       MovDir[x][y] = back_dir;
7643
7644     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7645   }
7646   else if (move_pattern == MV_HORIZONTAL ||
7647            move_pattern == MV_VERTICAL)
7648   {
7649     if (move_pattern & old_move_dir)
7650       MovDir[x][y] = back_dir;
7651     else if (move_pattern == MV_HORIZONTAL)
7652       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7653     else if (move_pattern == MV_VERTICAL)
7654       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7655
7656     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7657   }
7658   else if (move_pattern & MV_ANY_DIRECTION)
7659   {
7660     MovDir[x][y] = move_pattern;
7661     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7662   }
7663   else if (move_pattern & MV_WIND_DIRECTION)
7664   {
7665     MovDir[x][y] = game.wind_direction;
7666     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7667   }
7668   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7669   {
7670     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7671       MovDir[x][y] = left_dir;
7672     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7673       MovDir[x][y] = right_dir;
7674
7675     if (MovDir[x][y] != old_move_dir)
7676       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7677   }
7678   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7679   {
7680     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7681       MovDir[x][y] = right_dir;
7682     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7683       MovDir[x][y] = left_dir;
7684
7685     if (MovDir[x][y] != old_move_dir)
7686       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7687   }
7688   else if (move_pattern == MV_TOWARDS_PLAYER ||
7689            move_pattern == MV_AWAY_FROM_PLAYER)
7690   {
7691     int attr_x = -1, attr_y = -1;
7692     int newx, newy;
7693     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7694
7695     if (game.all_players_gone)
7696     {
7697       attr_x = game.exit_x;
7698       attr_y = game.exit_y;
7699     }
7700     else
7701     {
7702       int i;
7703
7704       for (i = 0; i < MAX_PLAYERS; i++)
7705       {
7706         struct PlayerInfo *player = &stored_player[i];
7707         int jx = player->jx, jy = player->jy;
7708
7709         if (!player->active)
7710           continue;
7711
7712         if (attr_x == -1 ||
7713             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7714         {
7715           attr_x = jx;
7716           attr_y = jy;
7717         }
7718       }
7719     }
7720
7721     MovDir[x][y] = MV_NONE;
7722     if (attr_x < x)
7723       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7724     else if (attr_x > x)
7725       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7726     if (attr_y < y)
7727       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7728     else if (attr_y > y)
7729       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7730
7731     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7732
7733     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7734     {
7735       boolean first_horiz = RND(2);
7736       int new_move_dir = MovDir[x][y];
7737
7738       if (element_info[element].move_stepsize == 0)     // "not moving"
7739       {
7740         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7741         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7742
7743         return;
7744       }
7745
7746       MovDir[x][y] =
7747         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7748       Moving2Blocked(x, y, &newx, &newy);
7749
7750       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7751         return;
7752
7753       MovDir[x][y] =
7754         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7755       Moving2Blocked(x, y, &newx, &newy);
7756
7757       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7758         return;
7759
7760       MovDir[x][y] = old_move_dir;
7761     }
7762   }
7763   else if (move_pattern == MV_WHEN_PUSHED ||
7764            move_pattern == MV_WHEN_DROPPED)
7765   {
7766     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7767       MovDir[x][y] = MV_NONE;
7768
7769     MovDelay[x][y] = 0;
7770   }
7771   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7772   {
7773     struct XY *test_xy = xy_topdown;
7774     static int test_dir[4] =
7775     {
7776       MV_UP,
7777       MV_LEFT,
7778       MV_RIGHT,
7779       MV_DOWN
7780     };
7781     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7782     int move_preference = -1000000;     // start with very low preference
7783     int new_move_dir = MV_NONE;
7784     int start_test = RND(4);
7785     int i;
7786
7787     for (i = 0; i < NUM_DIRECTIONS; i++)
7788     {
7789       int j = (start_test + i) % 4;
7790       int move_dir = test_dir[j];
7791       int move_dir_preference;
7792
7793       xx = x + test_xy[j].x;
7794       yy = y + test_xy[j].y;
7795
7796       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7797           (IS_PLAYER(xx, yy) || Tile[xx][yy] == EL_PLAYER_IS_LEAVING))
7798       {
7799         new_move_dir = move_dir;
7800
7801         break;
7802       }
7803
7804       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7805         continue;
7806
7807       move_dir_preference = -1 * RunnerVisit[xx][yy];
7808       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7809         move_dir_preference = PlayerVisit[xx][yy];
7810
7811       if (move_dir_preference > move_preference)
7812       {
7813         // prefer field that has not been visited for the longest time
7814         move_preference = move_dir_preference;
7815         new_move_dir = move_dir;
7816       }
7817       else if (move_dir_preference == move_preference &&
7818                move_dir == old_move_dir)
7819       {
7820         // prefer last direction when all directions are preferred equally
7821         move_preference = move_dir_preference;
7822         new_move_dir = move_dir;
7823       }
7824     }
7825
7826     MovDir[x][y] = new_move_dir;
7827     if (old_move_dir != new_move_dir)
7828       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7829   }
7830 }
7831
7832 static void TurnRound(int x, int y)
7833 {
7834   int direction = MovDir[x][y];
7835
7836   TurnRoundExt(x, y);
7837
7838   GfxDir[x][y] = MovDir[x][y];
7839
7840   if (direction != MovDir[x][y])
7841     GfxFrame[x][y] = 0;
7842
7843   if (MovDelay[x][y])
7844     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7845
7846   ResetGfxFrame(x, y);
7847 }
7848
7849 static boolean JustBeingPushed(int x, int y)
7850 {
7851   int i;
7852
7853   for (i = 0; i < MAX_PLAYERS; i++)
7854   {
7855     struct PlayerInfo *player = &stored_player[i];
7856
7857     if (player->active && player->is_pushing && player->MovPos)
7858     {
7859       int next_jx = player->jx + (player->jx - player->last_jx);
7860       int next_jy = player->jy + (player->jy - player->last_jy);
7861
7862       if (x == next_jx && y == next_jy)
7863         return TRUE;
7864     }
7865   }
7866
7867   return FALSE;
7868 }
7869
7870 static void StartMoving(int x, int y)
7871 {
7872   boolean started_moving = FALSE;       // some elements can fall _and_ move
7873   int element = Tile[x][y];
7874
7875   if (Stop[x][y])
7876     return;
7877
7878   if (MovDelay[x][y] == 0)
7879     GfxAction[x][y] = ACTION_DEFAULT;
7880
7881   if (CAN_FALL(element) && y < lev_fieldy - 1)
7882   {
7883     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7884         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7885       if (JustBeingPushed(x, y))
7886         return;
7887
7888     if (element == EL_QUICKSAND_FULL)
7889     {
7890       if (IS_FREE(x, y + 1))
7891       {
7892         InitMovingField(x, y, MV_DOWN);
7893         started_moving = TRUE;
7894
7895         Tile[x][y] = EL_QUICKSAND_EMPTYING;
7896 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7897         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7898           Store[x][y] = EL_ROCK;
7899 #else
7900         Store[x][y] = EL_ROCK;
7901 #endif
7902
7903         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7904       }
7905       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7906       {
7907         if (!MovDelay[x][y])
7908         {
7909           MovDelay[x][y] = TILEY + 1;
7910
7911           ResetGfxAnimation(x, y);
7912           ResetGfxAnimation(x, y + 1);
7913         }
7914
7915         if (MovDelay[x][y])
7916         {
7917           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7918           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7919
7920           MovDelay[x][y]--;
7921           if (MovDelay[x][y])
7922             return;
7923         }
7924
7925         Tile[x][y] = EL_QUICKSAND_EMPTY;
7926         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7927         Store[x][y + 1] = Store[x][y];
7928         Store[x][y] = 0;
7929
7930         PlayLevelSoundAction(x, y, ACTION_FILLING);
7931       }
7932       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7933       {
7934         if (!MovDelay[x][y])
7935         {
7936           MovDelay[x][y] = TILEY + 1;
7937
7938           ResetGfxAnimation(x, y);
7939           ResetGfxAnimation(x, y + 1);
7940         }
7941
7942         if (MovDelay[x][y])
7943         {
7944           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7945           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7946
7947           MovDelay[x][y]--;
7948           if (MovDelay[x][y])
7949             return;
7950         }
7951
7952         Tile[x][y] = EL_QUICKSAND_EMPTY;
7953         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7954         Store[x][y + 1] = Store[x][y];
7955         Store[x][y] = 0;
7956
7957         PlayLevelSoundAction(x, y, ACTION_FILLING);
7958       }
7959     }
7960     else if (element == EL_QUICKSAND_FAST_FULL)
7961     {
7962       if (IS_FREE(x, y + 1))
7963       {
7964         InitMovingField(x, y, MV_DOWN);
7965         started_moving = TRUE;
7966
7967         Tile[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7968 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7969         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7970           Store[x][y] = EL_ROCK;
7971 #else
7972         Store[x][y] = EL_ROCK;
7973 #endif
7974
7975         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7976       }
7977       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7978       {
7979         if (!MovDelay[x][y])
7980         {
7981           MovDelay[x][y] = TILEY + 1;
7982
7983           ResetGfxAnimation(x, y);
7984           ResetGfxAnimation(x, y + 1);
7985         }
7986
7987         if (MovDelay[x][y])
7988         {
7989           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7990           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7991
7992           MovDelay[x][y]--;
7993           if (MovDelay[x][y])
7994             return;
7995         }
7996
7997         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7998         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7999         Store[x][y + 1] = Store[x][y];
8000         Store[x][y] = 0;
8001
8002         PlayLevelSoundAction(x, y, ACTION_FILLING);
8003       }
8004       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
8005       {
8006         if (!MovDelay[x][y])
8007         {
8008           MovDelay[x][y] = TILEY + 1;
8009
8010           ResetGfxAnimation(x, y);
8011           ResetGfxAnimation(x, y + 1);
8012         }
8013
8014         if (MovDelay[x][y])
8015         {
8016           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
8017           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
8018
8019           MovDelay[x][y]--;
8020           if (MovDelay[x][y])
8021             return;
8022         }
8023
8024         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
8025         Tile[x][y + 1] = EL_QUICKSAND_FULL;
8026         Store[x][y + 1] = Store[x][y];
8027         Store[x][y] = 0;
8028
8029         PlayLevelSoundAction(x, y, ACTION_FILLING);
8030       }
8031     }
8032     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8033              Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
8034     {
8035       InitMovingField(x, y, MV_DOWN);
8036       started_moving = TRUE;
8037
8038       Tile[x][y] = EL_QUICKSAND_FILLING;
8039       Store[x][y] = element;
8040
8041       PlayLevelSoundAction(x, y, ACTION_FILLING);
8042     }
8043     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8044              Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8045     {
8046       InitMovingField(x, y, MV_DOWN);
8047       started_moving = TRUE;
8048
8049       Tile[x][y] = EL_QUICKSAND_FAST_FILLING;
8050       Store[x][y] = element;
8051
8052       PlayLevelSoundAction(x, y, ACTION_FILLING);
8053     }
8054     else if (element == EL_MAGIC_WALL_FULL)
8055     {
8056       if (IS_FREE(x, y + 1))
8057       {
8058         InitMovingField(x, y, MV_DOWN);
8059         started_moving = TRUE;
8060
8061         Tile[x][y] = EL_MAGIC_WALL_EMPTYING;
8062         Store[x][y] = EL_CHANGED(Store[x][y]);
8063       }
8064       else if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
8065       {
8066         if (!MovDelay[x][y])
8067           MovDelay[x][y] = TILEY / 4 + 1;
8068
8069         if (MovDelay[x][y])
8070         {
8071           MovDelay[x][y]--;
8072           if (MovDelay[x][y])
8073             return;
8074         }
8075
8076         Tile[x][y] = EL_MAGIC_WALL_ACTIVE;
8077         Tile[x][y + 1] = EL_MAGIC_WALL_FULL;
8078         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
8079         Store[x][y] = 0;
8080       }
8081     }
8082     else if (element == EL_BD_MAGIC_WALL_FULL)
8083     {
8084       if (IS_FREE(x, y + 1))
8085       {
8086         InitMovingField(x, y, MV_DOWN);
8087         started_moving = TRUE;
8088
8089         Tile[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
8090         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
8091       }
8092       else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
8093       {
8094         if (!MovDelay[x][y])
8095           MovDelay[x][y] = TILEY / 4 + 1;
8096
8097         if (MovDelay[x][y])
8098         {
8099           MovDelay[x][y]--;
8100           if (MovDelay[x][y])
8101             return;
8102         }
8103
8104         Tile[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
8105         Tile[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
8106         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
8107         Store[x][y] = 0;
8108       }
8109     }
8110     else if (element == EL_DC_MAGIC_WALL_FULL)
8111     {
8112       if (IS_FREE(x, y + 1))
8113       {
8114         InitMovingField(x, y, MV_DOWN);
8115         started_moving = TRUE;
8116
8117         Tile[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
8118         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
8119       }
8120       else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
8121       {
8122         if (!MovDelay[x][y])
8123           MovDelay[x][y] = TILEY / 4 + 1;
8124
8125         if (MovDelay[x][y])
8126         {
8127           MovDelay[x][y]--;
8128           if (MovDelay[x][y])
8129             return;
8130         }
8131
8132         Tile[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
8133         Tile[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
8134         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
8135         Store[x][y] = 0;
8136       }
8137     }
8138     else if ((CAN_PASS_MAGIC_WALL(element) &&
8139               (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
8140                Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
8141              (CAN_PASS_DC_MAGIC_WALL(element) &&
8142               (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
8143
8144     {
8145       InitMovingField(x, y, MV_DOWN);
8146       started_moving = TRUE;
8147
8148       Tile[x][y] =
8149         (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
8150          Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
8151          EL_DC_MAGIC_WALL_FILLING);
8152       Store[x][y] = element;
8153     }
8154     else if (CAN_FALL(element) && Tile[x][y + 1] == EL_ACID)
8155     {
8156       SplashAcid(x, y + 1);
8157
8158       InitMovingField(x, y, MV_DOWN);
8159       started_moving = TRUE;
8160
8161       Store[x][y] = EL_ACID;
8162     }
8163     else if (
8164              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8165               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
8166              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
8167               CAN_FALL(element) && WasJustFalling[x][y] &&
8168               (Tile[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
8169
8170              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
8171               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
8172               (Tile[x][y + 1] == EL_BLOCKED)))
8173     {
8174       /* this is needed for a special case not covered by calling "Impact()"
8175          from "ContinueMoving()": if an element moves to a tile directly below
8176          another element which was just falling on that tile (which was empty
8177          in the previous frame), the falling element above would just stop
8178          instead of smashing the element below (in previous version, the above
8179          element was just checked for "moving" instead of "falling", resulting
8180          in incorrect smashes caused by horizontal movement of the above
8181          element; also, the case of the player being the element to smash was
8182          simply not covered here... :-/ ) */
8183
8184       CheckCollision[x][y] = 0;
8185       CheckImpact[x][y] = 0;
8186
8187       Impact(x, y);
8188     }
8189     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
8190     {
8191       if (MovDir[x][y] == MV_NONE)
8192       {
8193         InitMovingField(x, y, MV_DOWN);
8194         started_moving = TRUE;
8195       }
8196     }
8197     else if (IS_FREE(x, y + 1) || Tile[x][y + 1] == EL_DIAMOND_BREAKING)
8198     {
8199       if (WasJustFalling[x][y]) // prevent animation from being restarted
8200         MovDir[x][y] = MV_DOWN;
8201
8202       InitMovingField(x, y, MV_DOWN);
8203       started_moving = TRUE;
8204     }
8205     else if (element == EL_AMOEBA_DROP)
8206     {
8207       Tile[x][y] = EL_AMOEBA_GROWING;
8208       Store[x][y] = EL_AMOEBA_WET;
8209     }
8210     else if (((IS_SLIPPERY(Tile[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8211               (IS_EM_SLIPPERY_WALL(Tile[x][y + 1]) && IS_GEM(element))) &&
8212              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8213              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8214     {
8215       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
8216                                 (IS_FREE(x - 1, y + 1) ||
8217                                  Tile[x - 1][y + 1] == EL_ACID));
8218       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8219                                 (IS_FREE(x + 1, y + 1) ||
8220                                  Tile[x + 1][y + 1] == EL_ACID));
8221       boolean can_fall_any  = (can_fall_left || can_fall_right);
8222       boolean can_fall_both = (can_fall_left && can_fall_right);
8223       int slippery_type = element_info[Tile[x][y + 1]].slippery_type;
8224
8225       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8226       {
8227         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8228           can_fall_right = FALSE;
8229         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8230           can_fall_left = FALSE;
8231         else if (slippery_type == SLIPPERY_ONLY_LEFT)
8232           can_fall_right = FALSE;
8233         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8234           can_fall_left = FALSE;
8235
8236         can_fall_any  = (can_fall_left || can_fall_right);
8237         can_fall_both = FALSE;
8238       }
8239
8240       if (can_fall_both)
8241       {
8242         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8243           can_fall_right = FALSE;       // slip down on left side
8244         else
8245           can_fall_left = !(can_fall_right = RND(2));
8246
8247         can_fall_both = FALSE;
8248       }
8249
8250       if (can_fall_any)
8251       {
8252         // if not determined otherwise, prefer left side for slipping down
8253         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8254         started_moving = TRUE;
8255       }
8256     }
8257     else if (IS_BELT_ACTIVE(Tile[x][y + 1]))
8258     {
8259       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
8260       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8261       int belt_nr = getBeltNrFromBeltActiveElement(Tile[x][y + 1]);
8262       int belt_dir = game.belt_dir[belt_nr];
8263
8264       if ((belt_dir == MV_LEFT  && left_is_free) ||
8265           (belt_dir == MV_RIGHT && right_is_free))
8266       {
8267         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8268
8269         InitMovingField(x, y, belt_dir);
8270         started_moving = TRUE;
8271
8272         Pushed[x][y] = TRUE;
8273         Pushed[nextx][y] = TRUE;
8274
8275         GfxAction[x][y] = ACTION_DEFAULT;
8276       }
8277       else
8278       {
8279         MovDir[x][y] = 0;       // if element was moving, stop it
8280       }
8281     }
8282   }
8283
8284   // not "else if" because of elements that can fall and move (EL_SPRING)
8285   if (CAN_MOVE(element) && !started_moving)
8286   {
8287     int move_pattern = element_info[element].move_pattern;
8288     int newx, newy;
8289
8290     Moving2Blocked(x, y, &newx, &newy);
8291
8292     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8293       return;
8294
8295     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8296         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8297     {
8298       WasJustMoving[x][y] = 0;
8299       CheckCollision[x][y] = 0;
8300
8301       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8302
8303       if (Tile[x][y] != element)        // element has changed
8304         return;
8305     }
8306
8307     if (!MovDelay[x][y])        // start new movement phase
8308     {
8309       // all objects that can change their move direction after each step
8310       // (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall
8311
8312       if (element != EL_YAMYAM &&
8313           element != EL_DARK_YAMYAM &&
8314           element != EL_PACMAN &&
8315           !(move_pattern & MV_ANY_DIRECTION) &&
8316           move_pattern != MV_TURNING_LEFT &&
8317           move_pattern != MV_TURNING_RIGHT &&
8318           move_pattern != MV_TURNING_LEFT_RIGHT &&
8319           move_pattern != MV_TURNING_RIGHT_LEFT &&
8320           move_pattern != MV_TURNING_RANDOM)
8321       {
8322         TurnRound(x, y);
8323
8324         if (MovDelay[x][y] && (element == EL_BUG ||
8325                                element == EL_SPACESHIP ||
8326                                element == EL_SP_SNIKSNAK ||
8327                                element == EL_SP_ELECTRON ||
8328                                element == EL_MOLE))
8329           TEST_DrawLevelField(x, y);
8330       }
8331     }
8332
8333     if (MovDelay[x][y])         // wait some time before next movement
8334     {
8335       MovDelay[x][y]--;
8336
8337       if (element == EL_ROBOT ||
8338           element == EL_YAMYAM ||
8339           element == EL_DARK_YAMYAM)
8340       {
8341         DrawLevelElementAnimationIfNeeded(x, y, element);
8342         PlayLevelSoundAction(x, y, ACTION_WAITING);
8343       }
8344       else if (element == EL_SP_ELECTRON)
8345         DrawLevelElementAnimationIfNeeded(x, y, element);
8346       else if (element == EL_DRAGON)
8347       {
8348         int i;
8349         int dir = MovDir[x][y];
8350         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8351         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8352         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8353                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8354                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8355                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8356         int frame = getGraphicAnimationFrameXY(graphic, x, y);
8357
8358         GfxAction[x][y] = ACTION_ATTACKING;
8359
8360         if (IS_PLAYER(x, y))
8361           DrawPlayerField(x, y);
8362         else
8363           TEST_DrawLevelField(x, y);
8364
8365         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8366
8367         for (i = 1; i <= 3; i++)
8368         {
8369           int xx = x + i * dx;
8370           int yy = y + i * dy;
8371           int sx = SCREENX(xx);
8372           int sy = SCREENY(yy);
8373           int flame_graphic = graphic + (i - 1);
8374
8375           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Tile[xx][yy]))
8376             break;
8377
8378           if (MovDelay[x][y])
8379           {
8380             int flamed = MovingOrBlocked2Element(xx, yy);
8381
8382             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8383               Bang(xx, yy);
8384             else
8385               RemoveMovingField(xx, yy);
8386
8387             ChangeDelay[xx][yy] = 0;
8388
8389             Tile[xx][yy] = EL_FLAMES;
8390
8391             if (IN_SCR_FIELD(sx, sy))
8392             {
8393               TEST_DrawLevelFieldCrumbled(xx, yy);
8394               DrawScreenGraphic(sx, sy, flame_graphic, frame);
8395             }
8396           }
8397           else
8398           {
8399             if (Tile[xx][yy] == EL_FLAMES)
8400               Tile[xx][yy] = EL_EMPTY;
8401             TEST_DrawLevelField(xx, yy);
8402           }
8403         }
8404       }
8405
8406       if (MovDelay[x][y])       // element still has to wait some time
8407       {
8408         PlayLevelSoundAction(x, y, ACTION_WAITING);
8409
8410         return;
8411       }
8412     }
8413
8414     // now make next step
8415
8416     Moving2Blocked(x, y, &newx, &newy); // get next screen position
8417
8418     if (DONT_COLLIDE_WITH(element) &&
8419         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8420         !PLAYER_ENEMY_PROTECTED(newx, newy))
8421     {
8422       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8423
8424       return;
8425     }
8426
8427     else if (CAN_MOVE_INTO_ACID(element) &&
8428              IN_LEV_FIELD(newx, newy) && Tile[newx][newy] == EL_ACID &&
8429              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8430              (MovDir[x][y] == MV_DOWN ||
8431               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8432     {
8433       SplashAcid(newx, newy);
8434       Store[x][y] = EL_ACID;
8435     }
8436     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8437     {
8438       if (Tile[newx][newy] == EL_EXIT_OPEN ||
8439           Tile[newx][newy] == EL_EM_EXIT_OPEN ||
8440           Tile[newx][newy] == EL_STEEL_EXIT_OPEN ||
8441           Tile[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8442       {
8443         RemoveField(x, y);
8444         TEST_DrawLevelField(x, y);
8445
8446         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8447         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8448           DrawGraphicThruMask(SCREENX(newx), SCREENY(newy), el2img(element), 0);
8449
8450         game.friends_still_needed--;
8451         if (!game.friends_still_needed &&
8452             !game.GameOver &&
8453             game.all_players_gone)
8454           LevelSolved();
8455
8456         return;
8457       }
8458       else if (IS_FOOD_PENGUIN(Tile[newx][newy]))
8459       {
8460         if (DigField(local_player, x, y, newx, newy, 0, 0, DF_DIG) == MP_MOVING)
8461           TEST_DrawLevelField(newx, newy);
8462         else
8463           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8464       }
8465       else if (!IS_FREE(newx, newy))
8466       {
8467         GfxAction[x][y] = ACTION_WAITING;
8468
8469         if (IS_PLAYER(x, y))
8470           DrawPlayerField(x, y);
8471         else
8472           TEST_DrawLevelField(x, y);
8473
8474         return;
8475       }
8476     }
8477     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8478     {
8479       if (IS_FOOD_PIG(Tile[newx][newy]))
8480       {
8481         if (IS_MOVING(newx, newy))
8482           RemoveMovingField(newx, newy);
8483         else
8484         {
8485           Tile[newx][newy] = EL_EMPTY;
8486           TEST_DrawLevelField(newx, newy);
8487         }
8488
8489         PlayLevelSound(x, y, SND_PIG_DIGGING);
8490       }
8491       else if (!IS_FREE(newx, newy))
8492       {
8493         if (IS_PLAYER(x, y))
8494           DrawPlayerField(x, y);
8495         else
8496           TEST_DrawLevelField(x, y);
8497
8498         return;
8499       }
8500     }
8501     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8502     {
8503       if (Store[x][y] != EL_EMPTY)
8504       {
8505         boolean can_clone = FALSE;
8506         int xx, yy;
8507
8508         // check if element to clone is still there
8509         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8510         {
8511           if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == Store[x][y])
8512           {
8513             can_clone = TRUE;
8514
8515             break;
8516           }
8517         }
8518
8519         // cannot clone or target field not free anymore -- do not clone
8520         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8521           Store[x][y] = EL_EMPTY;
8522       }
8523
8524       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8525       {
8526         if (IS_MV_DIAGONAL(MovDir[x][y]))
8527         {
8528           int diagonal_move_dir = MovDir[x][y];
8529           int stored = Store[x][y];
8530           int change_delay = 8;
8531           int graphic;
8532
8533           // android is moving diagonally
8534
8535           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8536
8537           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8538           GfxElement[x][y] = EL_EMC_ANDROID;
8539           GfxAction[x][y] = ACTION_SHRINKING;
8540           GfxDir[x][y] = diagonal_move_dir;
8541           ChangeDelay[x][y] = change_delay;
8542
8543           if (Store[x][y] == EL_EMPTY)
8544             Store[x][y] = GfxElementEmpty[x][y];
8545
8546           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8547                                    GfxDir[x][y]);
8548
8549           DrawLevelGraphicAnimation(x, y, graphic);
8550           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8551
8552           if (Tile[newx][newy] == EL_ACID)
8553           {
8554             SplashAcid(newx, newy);
8555
8556             return;
8557           }
8558
8559           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8560
8561           Store[newx][newy] = EL_EMC_ANDROID;
8562           GfxElement[newx][newy] = EL_EMC_ANDROID;
8563           GfxAction[newx][newy] = ACTION_GROWING;
8564           GfxDir[newx][newy] = diagonal_move_dir;
8565           ChangeDelay[newx][newy] = change_delay;
8566
8567           graphic = el_act_dir2img(GfxElement[newx][newy],
8568                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8569
8570           DrawLevelGraphicAnimation(newx, newy, graphic);
8571           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8572
8573           return;
8574         }
8575         else
8576         {
8577           Tile[newx][newy] = EL_EMPTY;
8578           TEST_DrawLevelField(newx, newy);
8579
8580           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8581         }
8582       }
8583       else if (!IS_FREE(newx, newy))
8584       {
8585         return;
8586       }
8587     }
8588     else if (IS_CUSTOM_ELEMENT(element) &&
8589              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8590     {
8591       if (!DigFieldByCE(newx, newy, element))
8592         return;
8593
8594       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8595       {
8596         RunnerVisit[x][y] = FrameCounter;
8597         PlayerVisit[x][y] /= 8;         // expire player visit path
8598       }
8599     }
8600     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8601     {
8602       if (!IS_FREE(newx, newy))
8603       {
8604         if (IS_PLAYER(x, y))
8605           DrawPlayerField(x, y);
8606         else
8607           TEST_DrawLevelField(x, y);
8608
8609         return;
8610       }
8611       else
8612       {
8613         boolean wanna_flame = !RND(10);
8614         int dx = newx - x, dy = newy - y;
8615         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8616         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8617         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8618                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8619         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8620                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8621
8622         if ((wanna_flame ||
8623              IS_CLASSIC_ENEMY(element1) ||
8624              IS_CLASSIC_ENEMY(element2)) &&
8625             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8626             element1 != EL_FLAMES && element2 != EL_FLAMES)
8627         {
8628           ResetGfxAnimation(x, y);
8629           GfxAction[x][y] = ACTION_ATTACKING;
8630
8631           if (IS_PLAYER(x, y))
8632             DrawPlayerField(x, y);
8633           else
8634             TEST_DrawLevelField(x, y);
8635
8636           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8637
8638           MovDelay[x][y] = 50;
8639
8640           Tile[newx][newy] = EL_FLAMES;
8641           if (IN_LEV_FIELD(newx1, newy1) && Tile[newx1][newy1] == EL_EMPTY)
8642             Tile[newx1][newy1] = EL_FLAMES;
8643           if (IN_LEV_FIELD(newx2, newy2) && Tile[newx2][newy2] == EL_EMPTY)
8644             Tile[newx2][newy2] = EL_FLAMES;
8645
8646           return;
8647         }
8648       }
8649     }
8650     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8651              Tile[newx][newy] == EL_DIAMOND)
8652     {
8653       if (IS_MOVING(newx, newy))
8654         RemoveMovingField(newx, newy);
8655       else
8656       {
8657         Tile[newx][newy] = EL_EMPTY;
8658         TEST_DrawLevelField(newx, newy);
8659       }
8660
8661       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8662     }
8663     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8664              IS_FOOD_DARK_YAMYAM(Tile[newx][newy]))
8665     {
8666       if (AmoebaNr[newx][newy])
8667       {
8668         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8669         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8670             Tile[newx][newy] == EL_BD_AMOEBA)
8671           AmoebaCnt[AmoebaNr[newx][newy]]--;
8672       }
8673
8674       if (IS_MOVING(newx, newy))
8675       {
8676         RemoveMovingField(newx, newy);
8677       }
8678       else
8679       {
8680         Tile[newx][newy] = EL_EMPTY;
8681         TEST_DrawLevelField(newx, newy);
8682       }
8683
8684       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8685     }
8686     else if ((element == EL_PACMAN || element == EL_MOLE)
8687              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Tile[newx][newy]))
8688     {
8689       if (AmoebaNr[newx][newy])
8690       {
8691         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8692         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8693             Tile[newx][newy] == EL_BD_AMOEBA)
8694           AmoebaCnt[AmoebaNr[newx][newy]]--;
8695       }
8696
8697       if (element == EL_MOLE)
8698       {
8699         Tile[newx][newy] = EL_AMOEBA_SHRINKING;
8700         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8701
8702         ResetGfxAnimation(x, y);
8703         GfxAction[x][y] = ACTION_DIGGING;
8704         TEST_DrawLevelField(x, y);
8705
8706         MovDelay[newx][newy] = 0;       // start amoeba shrinking delay
8707
8708         return;                         // wait for shrinking amoeba
8709       }
8710       else      // element == EL_PACMAN
8711       {
8712         Tile[newx][newy] = EL_EMPTY;
8713         TEST_DrawLevelField(newx, newy);
8714         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8715       }
8716     }
8717     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8718              (Tile[newx][newy] == EL_AMOEBA_SHRINKING ||
8719               (Tile[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8720     {
8721       // wait for shrinking amoeba to completely disappear
8722       return;
8723     }
8724     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8725     {
8726       // object was running against a wall
8727
8728       TurnRound(x, y);
8729
8730       if (GFX_ELEMENT(element) != EL_SAND)     // !!! FIX THIS (crumble) !!!
8731         DrawLevelElementAnimation(x, y, element);
8732
8733       if (DONT_TOUCH(element))
8734         TestIfBadThingTouchesPlayer(x, y);
8735
8736       return;
8737     }
8738
8739     InitMovingField(x, y, MovDir[x][y]);
8740
8741     PlayLevelSoundAction(x, y, ACTION_MOVING);
8742   }
8743
8744   if (MovDir[x][y])
8745     ContinueMoving(x, y);
8746 }
8747
8748 void ContinueMoving(int x, int y)
8749 {
8750   int element = Tile[x][y];
8751   struct ElementInfo *ei = &element_info[element];
8752   int direction = MovDir[x][y];
8753   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8754   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8755   int newx = x + dx, newy = y + dy;
8756   int stored = Store[x][y];
8757   int stored_new = Store[newx][newy];
8758   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8759   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8760   boolean last_line = (newy == lev_fieldy - 1);
8761   boolean use_step_delay = (GET_MAX_STEP_DELAY(element) != 0);
8762
8763   if (pushed_by_player)         // special case: moving object pushed by player
8764   {
8765     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x, y)->MovPos));
8766   }
8767   else if (use_step_delay)      // special case: moving object has step delay
8768   {
8769     if (!MovDelay[x][y])
8770       MovPos[x][y] += getElementMoveStepsize(x, y);
8771
8772     if (MovDelay[x][y])
8773       MovDelay[x][y]--;
8774     else
8775       MovDelay[x][y] = GET_NEW_STEP_DELAY(element);
8776
8777     if (MovDelay[x][y])
8778     {
8779       TEST_DrawLevelField(x, y);
8780
8781       return;   // element is still waiting
8782     }
8783   }
8784   else                          // normal case: generically moving object
8785   {
8786     MovPos[x][y] += getElementMoveStepsize(x, y);
8787   }
8788
8789   if (ABS(MovPos[x][y]) < TILEX)
8790   {
8791     TEST_DrawLevelField(x, y);
8792
8793     return;     // element is still moving
8794   }
8795
8796   // element reached destination field
8797
8798   Tile[x][y] = EL_EMPTY;
8799   Tile[newx][newy] = element;
8800   MovPos[x][y] = 0;     // force "not moving" for "crumbled sand"
8801
8802   if (Store[x][y] == EL_ACID)   // element is moving into acid pool
8803   {
8804     element = Tile[newx][newy] = EL_ACID;
8805   }
8806   else if (element == EL_MOLE)
8807   {
8808     Tile[x][y] = EL_SAND;
8809
8810     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8811   }
8812   else if (element == EL_QUICKSAND_FILLING)
8813   {
8814     element = Tile[newx][newy] = get_next_element(element);
8815     Store[newx][newy] = Store[x][y];
8816   }
8817   else if (element == EL_QUICKSAND_EMPTYING)
8818   {
8819     Tile[x][y] = get_next_element(element);
8820     element = Tile[newx][newy] = Store[x][y];
8821   }
8822   else if (element == EL_QUICKSAND_FAST_FILLING)
8823   {
8824     element = Tile[newx][newy] = get_next_element(element);
8825     Store[newx][newy] = Store[x][y];
8826   }
8827   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8828   {
8829     Tile[x][y] = get_next_element(element);
8830     element = Tile[newx][newy] = Store[x][y];
8831   }
8832   else if (element == EL_MAGIC_WALL_FILLING)
8833   {
8834     element = Tile[newx][newy] = get_next_element(element);
8835     if (!game.magic_wall_active)
8836       element = Tile[newx][newy] = EL_MAGIC_WALL_DEAD;
8837     Store[newx][newy] = Store[x][y];
8838   }
8839   else if (element == EL_MAGIC_WALL_EMPTYING)
8840   {
8841     Tile[x][y] = get_next_element(element);
8842     if (!game.magic_wall_active)
8843       Tile[x][y] = EL_MAGIC_WALL_DEAD;
8844     element = Tile[newx][newy] = Store[x][y];
8845
8846     InitField(newx, newy, FALSE);
8847   }
8848   else if (element == EL_BD_MAGIC_WALL_FILLING)
8849   {
8850     element = Tile[newx][newy] = get_next_element(element);
8851     if (!game.magic_wall_active)
8852       element = Tile[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8853     Store[newx][newy] = Store[x][y];
8854   }
8855   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8856   {
8857     Tile[x][y] = get_next_element(element);
8858     if (!game.magic_wall_active)
8859       Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
8860     element = Tile[newx][newy] = Store[x][y];
8861
8862     InitField(newx, newy, FALSE);
8863   }
8864   else if (element == EL_DC_MAGIC_WALL_FILLING)
8865   {
8866     element = Tile[newx][newy] = get_next_element(element);
8867     if (!game.magic_wall_active)
8868       element = Tile[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8869     Store[newx][newy] = Store[x][y];
8870   }
8871   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8872   {
8873     Tile[x][y] = get_next_element(element);
8874     if (!game.magic_wall_active)
8875       Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
8876     element = Tile[newx][newy] = Store[x][y];
8877
8878     InitField(newx, newy, FALSE);
8879   }
8880   else if (element == EL_AMOEBA_DROPPING)
8881   {
8882     Tile[x][y] = get_next_element(element);
8883     element = Tile[newx][newy] = Store[x][y];
8884   }
8885   else if (element == EL_SOKOBAN_OBJECT)
8886   {
8887     if (Back[x][y])
8888       Tile[x][y] = Back[x][y];
8889
8890     if (Back[newx][newy])
8891       Tile[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8892
8893     Back[x][y] = Back[newx][newy] = 0;
8894   }
8895
8896   Store[x][y] = EL_EMPTY;
8897   MovPos[x][y] = 0;
8898   MovDir[x][y] = 0;
8899   MovDelay[x][y] = 0;
8900
8901   MovDelay[newx][newy] = 0;
8902
8903   if (CAN_CHANGE_OR_HAS_ACTION(element))
8904   {
8905     // copy element change control values to new field
8906     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8907     ChangePage[newx][newy]  = ChangePage[x][y];
8908     ChangeCount[newx][newy] = ChangeCount[x][y];
8909     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8910   }
8911
8912   CustomValue[newx][newy] = CustomValue[x][y];
8913
8914   ChangeDelay[x][y] = 0;
8915   ChangePage[x][y] = -1;
8916   ChangeCount[x][y] = 0;
8917   ChangeEvent[x][y] = -1;
8918
8919   CustomValue[x][y] = 0;
8920
8921   // copy animation control values to new field
8922   GfxFrame[newx][newy]  = GfxFrame[x][y];
8923   GfxRandom[newx][newy] = GfxRandom[x][y];      // keep same random value
8924   GfxAction[newx][newy] = GfxAction[x][y];      // keep action one frame
8925   GfxDir[newx][newy]    = GfxDir[x][y];         // keep element direction
8926
8927   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8928
8929   // some elements can leave other elements behind after moving
8930   if (ei->move_leave_element != EL_EMPTY &&
8931       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8932       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8933   {
8934     int move_leave_element = ei->move_leave_element;
8935
8936     // this makes it possible to leave the removed element again
8937     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8938       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8939
8940     Tile[x][y] = move_leave_element;
8941
8942     if (element_info[Tile[x][y]].move_direction_initial == MV_START_PREVIOUS)
8943       MovDir[x][y] = direction;
8944
8945     InitField(x, y, FALSE);
8946
8947     if (GFX_CRUMBLED(Tile[x][y]))
8948       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8949
8950     if (IS_PLAYER_ELEMENT(move_leave_element))
8951       RelocatePlayer(x, y, move_leave_element);
8952   }
8953
8954   // do this after checking for left-behind element
8955   ResetGfxAnimation(x, y);      // reset animation values for old field
8956
8957   if (!CAN_MOVE(element) ||
8958       (CAN_FALL(element) && direction == MV_DOWN &&
8959        (element == EL_SPRING ||
8960         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8961         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8962     GfxDir[x][y] = MovDir[newx][newy] = 0;
8963
8964   TEST_DrawLevelField(x, y);
8965   TEST_DrawLevelField(newx, newy);
8966
8967   Stop[newx][newy] = TRUE;      // ignore this element until the next frame
8968
8969   // prevent pushed element from moving on in pushed direction
8970   if (pushed_by_player && CAN_MOVE(element) &&
8971       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8972       !(element_info[element].move_pattern & direction))
8973     TurnRound(newx, newy);
8974
8975   // prevent elements on conveyor belt from moving on in last direction
8976   if (pushed_by_conveyor && CAN_FALL(element) &&
8977       direction & MV_HORIZONTAL)
8978     MovDir[newx][newy] = 0;
8979
8980   if (!pushed_by_player)
8981   {
8982     int nextx = newx + dx, nexty = newy + dy;
8983     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8984
8985     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8986
8987     if (CAN_FALL(element) && direction == MV_DOWN)
8988       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8989
8990     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8991       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8992
8993     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8994       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8995   }
8996
8997   if (DONT_TOUCH(element))      // object may be nasty to player or others
8998   {
8999     TestIfBadThingTouchesPlayer(newx, newy);
9000     TestIfBadThingTouchesFriend(newx, newy);
9001
9002     if (!IS_CUSTOM_ELEMENT(element))
9003       TestIfBadThingTouchesOtherBadThing(newx, newy);
9004   }
9005   else if (element == EL_PENGUIN)
9006     TestIfFriendTouchesBadThing(newx, newy);
9007
9008   if (DONT_GET_HIT_BY(element))
9009   {
9010     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
9011   }
9012
9013   // give the player one last chance (one more frame) to move away
9014   if (CAN_FALL(element) && direction == MV_DOWN &&
9015       (last_line || (!IS_FREE(x, newy + 1) &&
9016                      (!IS_PLAYER(x, newy + 1) ||
9017                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
9018     Impact(x, newy);
9019
9020   if (pushed_by_player && !game.use_change_when_pushing_bug)
9021   {
9022     int push_side = MV_DIR_OPPOSITE(direction);
9023     struct PlayerInfo *player = PLAYERINFO(x, y);
9024
9025     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
9026                                player->index_bit, push_side);
9027     CheckTriggeredElementChangeByPlayer(newx, newy, element, CE_PLAYER_PUSHES_X,
9028                                         player->index_bit, push_side);
9029   }
9030
9031   if (element == EL_EMC_ANDROID && pushed_by_player)    // make another move
9032     MovDelay[newx][newy] = 1;
9033
9034   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
9035
9036   TestIfElementTouchesCustomElement(x, y);      // empty or new element
9037   TestIfElementHitsCustomElement(newx, newy, direction);
9038   TestIfPlayerTouchesCustomElement(newx, newy);
9039   TestIfElementTouchesCustomElement(newx, newy);
9040
9041   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
9042       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
9043     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
9044                              MV_DIR_OPPOSITE(direction));
9045 }
9046
9047 int AmoebaNeighbourNr(int ax, int ay)
9048 {
9049   int i;
9050   int element = Tile[ax][ay];
9051   int group_nr = 0;
9052   struct XY *xy = xy_topdown;
9053
9054   for (i = 0; i < NUM_DIRECTIONS; i++)
9055   {
9056     int x = ax + xy[i].x;
9057     int y = ay + xy[i].y;
9058
9059     if (!IN_LEV_FIELD(x, y))
9060       continue;
9061
9062     if (Tile[x][y] == element && AmoebaNr[x][y] > 0)
9063       group_nr = AmoebaNr[x][y];
9064   }
9065
9066   return group_nr;
9067 }
9068
9069 static void AmoebaMerge(int ax, int ay)
9070 {
9071   int i, x, y, xx, yy;
9072   int new_group_nr = AmoebaNr[ax][ay];
9073   struct XY *xy = xy_topdown;
9074
9075   if (new_group_nr == 0)
9076     return;
9077
9078   for (i = 0; i < NUM_DIRECTIONS; i++)
9079   {
9080     x = ax + xy[i].x;
9081     y = ay + xy[i].y;
9082
9083     if (!IN_LEV_FIELD(x, y))
9084       continue;
9085
9086     if ((Tile[x][y] == EL_AMOEBA_FULL ||
9087          Tile[x][y] == EL_BD_AMOEBA ||
9088          Tile[x][y] == EL_AMOEBA_DEAD) &&
9089         AmoebaNr[x][y] != new_group_nr)
9090     {
9091       int old_group_nr = AmoebaNr[x][y];
9092
9093       if (old_group_nr == 0)
9094         return;
9095
9096       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
9097       AmoebaCnt[old_group_nr] = 0;
9098       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
9099       AmoebaCnt2[old_group_nr] = 0;
9100
9101       SCAN_PLAYFIELD(xx, yy)
9102       {
9103         if (AmoebaNr[xx][yy] == old_group_nr)
9104           AmoebaNr[xx][yy] = new_group_nr;
9105       }
9106     }
9107   }
9108 }
9109
9110 void AmoebaToDiamond(int ax, int ay)
9111 {
9112   int i, x, y;
9113
9114   if (Tile[ax][ay] == EL_AMOEBA_DEAD)
9115   {
9116     int group_nr = AmoebaNr[ax][ay];
9117
9118 #ifdef DEBUG
9119     if (group_nr == 0)
9120     {
9121       Debug("game:playing:AmoebaToDiamond", "ax = %d, ay = %d", ax, ay);
9122       Debug("game:playing:AmoebaToDiamond", "This should never happen!");
9123
9124       return;
9125     }
9126 #endif
9127
9128     SCAN_PLAYFIELD(x, y)
9129     {
9130       if (Tile[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9131       {
9132         AmoebaNr[x][y] = 0;
9133         Tile[x][y] = EL_AMOEBA_TO_DIAMOND;
9134       }
9135     }
9136
9137     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9138                             SND_AMOEBA_TURNING_TO_GEM :
9139                             SND_AMOEBA_TURNING_TO_ROCK));
9140     Bang(ax, ay);
9141   }
9142   else
9143   {
9144     struct XY *xy = xy_topdown;
9145
9146     for (i = 0; i < NUM_DIRECTIONS; i++)
9147     {
9148       x = ax + xy[i].x;
9149       y = ay + xy[i].y;
9150
9151       if (!IN_LEV_FIELD(x, y))
9152         continue;
9153
9154       if (Tile[x][y] == EL_AMOEBA_TO_DIAMOND)
9155       {
9156         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9157                               SND_AMOEBA_TURNING_TO_GEM :
9158                               SND_AMOEBA_TURNING_TO_ROCK));
9159         Bang(x, y);
9160       }
9161     }
9162   }
9163 }
9164
9165 static void AmoebaToDiamondBD(int ax, int ay, int new_element)
9166 {
9167   int x, y;
9168   int group_nr = AmoebaNr[ax][ay];
9169   boolean done = FALSE;
9170
9171 #ifdef DEBUG
9172   if (group_nr == 0)
9173   {
9174     Debug("game:playing:AmoebaToDiamondBD", "ax = %d, ay = %d", ax, ay);
9175     Debug("game:playing:AmoebaToDiamondBD", "This should never happen!");
9176
9177     return;
9178   }
9179 #endif
9180
9181   SCAN_PLAYFIELD(x, y)
9182   {
9183     if (AmoebaNr[x][y] == group_nr &&
9184         (Tile[x][y] == EL_AMOEBA_DEAD ||
9185          Tile[x][y] == EL_BD_AMOEBA ||
9186          Tile[x][y] == EL_AMOEBA_GROWING))
9187     {
9188       AmoebaNr[x][y] = 0;
9189       Tile[x][y] = new_element;
9190       InitField(x, y, FALSE);
9191       TEST_DrawLevelField(x, y);
9192       done = TRUE;
9193     }
9194   }
9195
9196   if (done)
9197     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9198                             SND_BD_AMOEBA_TURNING_TO_ROCK :
9199                             SND_BD_AMOEBA_TURNING_TO_GEM));
9200 }
9201
9202 static void AmoebaGrowing(int x, int y)
9203 {
9204   static DelayCounter sound_delay = { 0 };
9205
9206   if (!MovDelay[x][y])          // start new growing cycle
9207   {
9208     MovDelay[x][y] = 7;
9209
9210     if (DelayReached(&sound_delay))
9211     {
9212       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9213       sound_delay.value = 30;
9214     }
9215   }
9216
9217   if (MovDelay[x][y])           // wait some time before growing bigger
9218   {
9219     MovDelay[x][y]--;
9220     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9221     {
9222       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9223                                            6 - MovDelay[x][y]);
9224
9225       DrawLevelGraphic(x, y, IMG_AMOEBA_GROWING, frame);
9226     }
9227
9228     if (!MovDelay[x][y])
9229     {
9230       Tile[x][y] = Store[x][y];
9231       Store[x][y] = 0;
9232       TEST_DrawLevelField(x, y);
9233     }
9234   }
9235 }
9236
9237 static void AmoebaShrinking(int x, int y)
9238 {
9239   static DelayCounter sound_delay = { 0 };
9240
9241   if (!MovDelay[x][y])          // start new shrinking cycle
9242   {
9243     MovDelay[x][y] = 7;
9244
9245     if (DelayReached(&sound_delay))
9246       sound_delay.value = 30;
9247   }
9248
9249   if (MovDelay[x][y])           // wait some time before shrinking
9250   {
9251     MovDelay[x][y]--;
9252     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9253     {
9254       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9255                                            6 - MovDelay[x][y]);
9256
9257       DrawLevelGraphic(x, y, IMG_AMOEBA_SHRINKING, frame);
9258     }
9259
9260     if (!MovDelay[x][y])
9261     {
9262       Tile[x][y] = EL_EMPTY;
9263       TEST_DrawLevelField(x, y);
9264
9265       // don't let mole enter this field in this cycle;
9266       // (give priority to objects falling to this field from above)
9267       Stop[x][y] = TRUE;
9268     }
9269   }
9270 }
9271
9272 static void AmoebaReproduce(int ax, int ay)
9273 {
9274   int i;
9275   int element = Tile[ax][ay];
9276   int graphic = el2img(element);
9277   int newax = ax, neway = ay;
9278   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9279   struct XY *xy = xy_topdown;
9280
9281   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9282   {
9283     Tile[ax][ay] = EL_AMOEBA_DEAD;
9284     TEST_DrawLevelField(ax, ay);
9285     return;
9286   }
9287
9288   if (IS_ANIMATED(graphic))
9289     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9290
9291   if (!MovDelay[ax][ay])        // start making new amoeba field
9292     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9293
9294   if (MovDelay[ax][ay])         // wait some time before making new amoeba
9295   {
9296     MovDelay[ax][ay]--;
9297     if (MovDelay[ax][ay])
9298       return;
9299   }
9300
9301   if (can_drop)                 // EL_AMOEBA_WET or EL_EMC_DRIPPER
9302   {
9303     int start = RND(4);
9304     int x = ax + xy[start].x;
9305     int y = ay + xy[start].y;
9306
9307     if (!IN_LEV_FIELD(x, y))
9308       return;
9309
9310     if (IS_FREE(x, y) ||
9311         CAN_GROW_INTO(Tile[x][y]) ||
9312         Tile[x][y] == EL_QUICKSAND_EMPTY ||
9313         Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9314     {
9315       newax = x;
9316       neway = y;
9317     }
9318
9319     if (newax == ax && neway == ay)
9320       return;
9321   }
9322   else                          // normal or "filled" (BD style) amoeba
9323   {
9324     int start = RND(4);
9325     boolean waiting_for_player = FALSE;
9326
9327     for (i = 0; i < NUM_DIRECTIONS; i++)
9328     {
9329       int j = (start + i) % 4;
9330       int x = ax + xy[j].x;
9331       int y = ay + xy[j].y;
9332
9333       if (!IN_LEV_FIELD(x, y))
9334         continue;
9335
9336       if (IS_FREE(x, y) ||
9337           CAN_GROW_INTO(Tile[x][y]) ||
9338           Tile[x][y] == EL_QUICKSAND_EMPTY ||
9339           Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9340       {
9341         newax = x;
9342         neway = y;
9343         break;
9344       }
9345       else if (IS_PLAYER(x, y))
9346         waiting_for_player = TRUE;
9347     }
9348
9349     if (newax == ax && neway == ay)             // amoeba cannot grow
9350     {
9351       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9352       {
9353         Tile[ax][ay] = EL_AMOEBA_DEAD;
9354         TEST_DrawLevelField(ax, ay);
9355         AmoebaCnt[AmoebaNr[ax][ay]]--;
9356
9357         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   // amoeba is completely dead
9358         {
9359           if (element == EL_AMOEBA_FULL)
9360             AmoebaToDiamond(ax, ay);
9361           else if (element == EL_BD_AMOEBA)
9362             AmoebaToDiamondBD(ax, ay, level.amoeba_content);
9363         }
9364       }
9365       return;
9366     }
9367     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9368     {
9369       // amoeba gets larger by growing in some direction
9370
9371       int new_group_nr = AmoebaNr[ax][ay];
9372
9373 #ifdef DEBUG
9374   if (new_group_nr == 0)
9375   {
9376     Debug("game:playing:AmoebaReproduce", "newax = %d, neway = %d",
9377           newax, neway);
9378     Debug("game:playing:AmoebaReproduce", "This should never happen!");
9379
9380     return;
9381   }
9382 #endif
9383
9384       AmoebaNr[newax][neway] = new_group_nr;
9385       AmoebaCnt[new_group_nr]++;
9386       AmoebaCnt2[new_group_nr]++;
9387
9388       // if amoeba touches other amoeba(s) after growing, unify them
9389       AmoebaMerge(newax, neway);
9390
9391       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9392       {
9393         AmoebaToDiamondBD(newax, neway, EL_BD_ROCK);
9394         return;
9395       }
9396     }
9397   }
9398
9399   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9400       (neway == lev_fieldy - 1 && newax != ax))
9401   {
9402     Tile[newax][neway] = EL_AMOEBA_GROWING;     // creation of new amoeba
9403     Store[newax][neway] = element;
9404   }
9405   else if (neway == ay || element == EL_EMC_DRIPPER)
9406   {
9407     Tile[newax][neway] = EL_AMOEBA_DROP;        // drop left/right of amoeba
9408
9409     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9410   }
9411   else
9412   {
9413     InitMovingField(ax, ay, MV_DOWN);           // drop dripping from amoeba
9414     Tile[ax][ay] = EL_AMOEBA_DROPPING;
9415     Store[ax][ay] = EL_AMOEBA_DROP;
9416     ContinueMoving(ax, ay);
9417     return;
9418   }
9419
9420   TEST_DrawLevelField(newax, neway);
9421 }
9422
9423 static void Life(int ax, int ay)
9424 {
9425   int x1, y1, x2, y2;
9426   int life_time = 40;
9427   int element = Tile[ax][ay];
9428   int graphic = el2img(element);
9429   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9430                          level.biomaze);
9431   boolean changed = FALSE;
9432
9433   if (IS_ANIMATED(graphic))
9434     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9435
9436   if (Stop[ax][ay])
9437     return;
9438
9439   if (!MovDelay[ax][ay])        // start new "game of life" cycle
9440     MovDelay[ax][ay] = life_time;
9441
9442   if (MovDelay[ax][ay])         // wait some time before next cycle
9443   {
9444     MovDelay[ax][ay]--;
9445     if (MovDelay[ax][ay])
9446       return;
9447   }
9448
9449   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9450   {
9451     int xx = ax+x1, yy = ay+y1;
9452     int old_element = Tile[xx][yy];
9453     int num_neighbours = 0;
9454
9455     if (!IN_LEV_FIELD(xx, yy))
9456       continue;
9457
9458     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9459     {
9460       int x = xx+x2, y = yy+y2;
9461
9462       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9463         continue;
9464
9465       boolean is_player_cell = (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y));
9466       boolean is_neighbour = FALSE;
9467
9468       if (level.use_life_bugs)
9469         is_neighbour =
9470           (((Tile[x][y] == element || is_player_cell) && !Stop[x][y]) ||
9471            (IS_FREE(x, y)                             &&  Stop[x][y]));
9472       else
9473         is_neighbour =
9474           (Last[x][y] == element || is_player_cell);
9475
9476       if (is_neighbour)
9477         num_neighbours++;
9478     }
9479
9480     boolean is_free = FALSE;
9481
9482     if (level.use_life_bugs)
9483       is_free = (IS_FREE(xx, yy));
9484     else
9485       is_free = (IS_FREE(xx, yy) && Last[xx][yy] == EL_EMPTY);
9486
9487     if (xx == ax && yy == ay)           // field in the middle
9488     {
9489       if (num_neighbours < life_parameter[0] ||
9490           num_neighbours > life_parameter[1])
9491       {
9492         Tile[xx][yy] = EL_EMPTY;
9493         if (Tile[xx][yy] != old_element)
9494           TEST_DrawLevelField(xx, yy);
9495         Stop[xx][yy] = TRUE;
9496         changed = TRUE;
9497       }
9498     }
9499     else if (is_free || CAN_GROW_INTO(Tile[xx][yy]))
9500     {                                   // free border field
9501       if (num_neighbours >= life_parameter[2] &&
9502           num_neighbours <= life_parameter[3])
9503       {
9504         Tile[xx][yy] = element;
9505         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time - 1);
9506         if (Tile[xx][yy] != old_element)
9507           TEST_DrawLevelField(xx, yy);
9508         Stop[xx][yy] = TRUE;
9509         changed = TRUE;
9510       }
9511     }
9512   }
9513
9514   if (changed)
9515     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9516                    SND_GAME_OF_LIFE_GROWING);
9517 }
9518
9519 static void InitRobotWheel(int x, int y)
9520 {
9521   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9522 }
9523
9524 static void RunRobotWheel(int x, int y)
9525 {
9526   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9527 }
9528
9529 static void StopRobotWheel(int x, int y)
9530 {
9531   if (game.robot_wheel_x == x &&
9532       game.robot_wheel_y == y)
9533   {
9534     game.robot_wheel_x = -1;
9535     game.robot_wheel_y = -1;
9536     game.robot_wheel_active = FALSE;
9537   }
9538 }
9539
9540 static void InitTimegateWheel(int x, int y)
9541 {
9542   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9543 }
9544
9545 static void RunTimegateWheel(int x, int y)
9546 {
9547   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9548 }
9549
9550 static void InitMagicBallDelay(int x, int y)
9551 {
9552   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9553 }
9554
9555 static void ActivateMagicBall(int bx, int by)
9556 {
9557   int x, y;
9558
9559   if (level.ball_random)
9560   {
9561     int pos_border = RND(8);    // select one of the eight border elements
9562     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9563     int xx = pos_content % 3;
9564     int yy = pos_content / 3;
9565
9566     x = bx - 1 + xx;
9567     y = by - 1 + yy;
9568
9569     if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9570       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9571   }
9572   else
9573   {
9574     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9575     {
9576       int xx = x - bx + 1;
9577       int yy = y - by + 1;
9578
9579       if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9580         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9581     }
9582   }
9583
9584   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9585 }
9586
9587 static void CheckExit(int x, int y)
9588 {
9589   if (game.gems_still_needed > 0 ||
9590       game.sokoban_fields_still_needed > 0 ||
9591       game.sokoban_objects_still_needed > 0 ||
9592       game.lights_still_needed > 0)
9593   {
9594     int element = Tile[x][y];
9595     int graphic = el2img(element);
9596
9597     if (IS_ANIMATED(graphic))
9598       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9599
9600     return;
9601   }
9602
9603   // do not re-open exit door closed after last player
9604   if (game.all_players_gone)
9605     return;
9606
9607   Tile[x][y] = EL_EXIT_OPENING;
9608
9609   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9610 }
9611
9612 static void CheckExitEM(int x, int y)
9613 {
9614   if (game.gems_still_needed > 0 ||
9615       game.sokoban_fields_still_needed > 0 ||
9616       game.sokoban_objects_still_needed > 0 ||
9617       game.lights_still_needed > 0)
9618   {
9619     int element = Tile[x][y];
9620     int graphic = el2img(element);
9621
9622     if (IS_ANIMATED(graphic))
9623       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9624
9625     return;
9626   }
9627
9628   // do not re-open exit door closed after last player
9629   if (game.all_players_gone)
9630     return;
9631
9632   Tile[x][y] = EL_EM_EXIT_OPENING;
9633
9634   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9635 }
9636
9637 static void CheckExitSteel(int x, int y)
9638 {
9639   if (game.gems_still_needed > 0 ||
9640       game.sokoban_fields_still_needed > 0 ||
9641       game.sokoban_objects_still_needed > 0 ||
9642       game.lights_still_needed > 0)
9643   {
9644     int element = Tile[x][y];
9645     int graphic = el2img(element);
9646
9647     if (IS_ANIMATED(graphic))
9648       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9649
9650     return;
9651   }
9652
9653   // do not re-open exit door closed after last player
9654   if (game.all_players_gone)
9655     return;
9656
9657   Tile[x][y] = EL_STEEL_EXIT_OPENING;
9658
9659   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9660 }
9661
9662 static void CheckExitSteelEM(int x, int y)
9663 {
9664   if (game.gems_still_needed > 0 ||
9665       game.sokoban_fields_still_needed > 0 ||
9666       game.sokoban_objects_still_needed > 0 ||
9667       game.lights_still_needed > 0)
9668   {
9669     int element = Tile[x][y];
9670     int graphic = el2img(element);
9671
9672     if (IS_ANIMATED(graphic))
9673       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9674
9675     return;
9676   }
9677
9678   // do not re-open exit door closed after last player
9679   if (game.all_players_gone)
9680     return;
9681
9682   Tile[x][y] = EL_EM_STEEL_EXIT_OPENING;
9683
9684   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9685 }
9686
9687 static void CheckExitSP(int x, int y)
9688 {
9689   if (game.gems_still_needed > 0)
9690   {
9691     int element = Tile[x][y];
9692     int graphic = el2img(element);
9693
9694     if (IS_ANIMATED(graphic))
9695       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9696
9697     return;
9698   }
9699
9700   // do not re-open exit door closed after last player
9701   if (game.all_players_gone)
9702     return;
9703
9704   Tile[x][y] = EL_SP_EXIT_OPENING;
9705
9706   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9707 }
9708
9709 static void CloseAllOpenTimegates(void)
9710 {
9711   int x, y;
9712
9713   SCAN_PLAYFIELD(x, y)
9714   {
9715     int element = Tile[x][y];
9716
9717     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9718     {
9719       Tile[x][y] = EL_TIMEGATE_CLOSING;
9720
9721       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9722     }
9723   }
9724 }
9725
9726 static void DrawTwinkleOnField(int x, int y)
9727 {
9728   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9729     return;
9730
9731   if (Tile[x][y] == EL_BD_DIAMOND)
9732     return;
9733
9734   if (MovDelay[x][y] == 0)      // next animation frame
9735     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9736
9737   if (MovDelay[x][y] != 0)      // wait some time before next frame
9738   {
9739     MovDelay[x][y]--;
9740
9741     DrawLevelElementAnimation(x, y, Tile[x][y]);
9742
9743     if (MovDelay[x][y] != 0)
9744     {
9745       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9746                                            10 - MovDelay[x][y]);
9747
9748       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9749     }
9750   }
9751 }
9752
9753 static void WallGrowing(int x, int y)
9754 {
9755   int delay = 6;
9756
9757   if (!MovDelay[x][y])          // next animation frame
9758     MovDelay[x][y] = 3 * delay;
9759
9760   if (MovDelay[x][y])           // wait some time before next frame
9761   {
9762     MovDelay[x][y]--;
9763
9764     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9765     {
9766       int graphic = el_dir2img(Tile[x][y], GfxDir[x][y]);
9767       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9768
9769       DrawLevelGraphic(x, y, graphic, frame);
9770     }
9771
9772     if (!MovDelay[x][y])
9773     {
9774       if (MovDir[x][y] == MV_LEFT)
9775       {
9776         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Tile[x - 1][y]))
9777           TEST_DrawLevelField(x - 1, y);
9778       }
9779       else if (MovDir[x][y] == MV_RIGHT)
9780       {
9781         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Tile[x + 1][y]))
9782           TEST_DrawLevelField(x + 1, y);
9783       }
9784       else if (MovDir[x][y] == MV_UP)
9785       {
9786         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Tile[x][y - 1]))
9787           TEST_DrawLevelField(x, y - 1);
9788       }
9789       else
9790       {
9791         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Tile[x][y + 1]))
9792           TEST_DrawLevelField(x, y + 1);
9793       }
9794
9795       Tile[x][y] = Store[x][y];
9796       Store[x][y] = 0;
9797       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9798       TEST_DrawLevelField(x, y);
9799     }
9800   }
9801 }
9802
9803 static void CheckWallGrowing(int ax, int ay)
9804 {
9805   int element = Tile[ax][ay];
9806   int graphic = el2img(element);
9807   boolean free_top    = FALSE;
9808   boolean free_bottom = FALSE;
9809   boolean free_left   = FALSE;
9810   boolean free_right  = FALSE;
9811   boolean stop_top    = FALSE;
9812   boolean stop_bottom = FALSE;
9813   boolean stop_left   = FALSE;
9814   boolean stop_right  = FALSE;
9815   boolean new_wall    = FALSE;
9816
9817   boolean is_steelwall  = (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9818                            element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9819                            element == EL_EXPANDABLE_STEELWALL_ANY);
9820
9821   boolean grow_vertical   = (element == EL_EXPANDABLE_WALL_VERTICAL ||
9822                              element == EL_EXPANDABLE_WALL_ANY ||
9823                              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9824                              element == EL_EXPANDABLE_STEELWALL_ANY);
9825
9826   boolean grow_horizontal = (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9827                              element == EL_EXPANDABLE_WALL_ANY ||
9828                              element == EL_EXPANDABLE_WALL ||
9829                              element == EL_BD_EXPANDABLE_WALL ||
9830                              element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9831                              element == EL_EXPANDABLE_STEELWALL_ANY);
9832
9833   boolean stop_vertical   = (element == EL_EXPANDABLE_WALL_VERTICAL ||
9834                              element == EL_EXPANDABLE_STEELWALL_VERTICAL);
9835
9836   boolean stop_horizontal = (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9837                              element == EL_EXPANDABLE_WALL ||
9838                              element == EL_EXPANDABLE_STEELWALL_HORIZONTAL);
9839
9840   int wall_growing = (is_steelwall ?
9841                       EL_EXPANDABLE_STEELWALL_GROWING :
9842                       EL_EXPANDABLE_WALL_GROWING);
9843
9844   int gfx_wall_growing_up    = (is_steelwall ?
9845                                 IMG_EXPANDABLE_STEELWALL_GROWING_UP :
9846                                 IMG_EXPANDABLE_WALL_GROWING_UP);
9847   int gfx_wall_growing_down  = (is_steelwall ?
9848                                 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN :
9849                                 IMG_EXPANDABLE_WALL_GROWING_DOWN);
9850   int gfx_wall_growing_left  = (is_steelwall ?
9851                                 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT :
9852                                 IMG_EXPANDABLE_WALL_GROWING_LEFT);
9853   int gfx_wall_growing_right = (is_steelwall ?
9854                                 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT :
9855                                 IMG_EXPANDABLE_WALL_GROWING_RIGHT);
9856
9857   if (IS_ANIMATED(graphic))
9858     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9859
9860   if (!MovDelay[ax][ay])        // start building new wall
9861     MovDelay[ax][ay] = 6;
9862
9863   if (MovDelay[ax][ay])         // wait some time before building new wall
9864   {
9865     MovDelay[ax][ay]--;
9866     if (MovDelay[ax][ay])
9867       return;
9868   }
9869
9870   if (IN_LEV_FIELD(ax, ay - 1) && IS_FREE(ax, ay - 1))
9871     free_top = TRUE;
9872   if (IN_LEV_FIELD(ax, ay + 1) && IS_FREE(ax, ay + 1))
9873     free_bottom = TRUE;
9874   if (IN_LEV_FIELD(ax - 1, ay) && IS_FREE(ax - 1, ay))
9875     free_left = TRUE;
9876   if (IN_LEV_FIELD(ax + 1, ay) && IS_FREE(ax + 1, ay))
9877     free_right = TRUE;
9878
9879   if (grow_vertical)
9880   {
9881     if (free_top)
9882     {
9883       Tile[ax][ay - 1] = wall_growing;
9884       Store[ax][ay - 1] = element;
9885       GfxDir[ax][ay - 1] = MovDir[ax][ay - 1] = MV_UP;
9886
9887       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay - 1)))
9888         DrawLevelGraphic(ax, ay - 1, gfx_wall_growing_up, 0);
9889
9890       new_wall = TRUE;
9891     }
9892
9893     if (free_bottom)
9894     {
9895       Tile[ax][ay + 1] = wall_growing;
9896       Store[ax][ay + 1] = element;
9897       GfxDir[ax][ay + 1] = MovDir[ax][ay + 1] = MV_DOWN;
9898
9899       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay + 1)))
9900         DrawLevelGraphic(ax, ay + 1, gfx_wall_growing_down, 0);
9901
9902       new_wall = TRUE;
9903     }
9904   }
9905
9906   if (grow_horizontal)
9907   {
9908     if (free_left)
9909     {
9910       Tile[ax - 1][ay] = wall_growing;
9911       Store[ax - 1][ay] = element;
9912       GfxDir[ax - 1][ay] = MovDir[ax - 1][ay] = MV_LEFT;
9913
9914       if (IN_SCR_FIELD(SCREENX(ax - 1), SCREENY(ay)))
9915         DrawLevelGraphic(ax - 1, ay, gfx_wall_growing_left, 0);
9916
9917       new_wall = TRUE;
9918     }
9919
9920     if (free_right)
9921     {
9922       Tile[ax + 1][ay] = wall_growing;
9923       Store[ax + 1][ay] = element;
9924       GfxDir[ax + 1][ay] = MovDir[ax + 1][ay] = MV_RIGHT;
9925
9926       if (IN_SCR_FIELD(SCREENX(ax + 1), SCREENY(ay)))
9927         DrawLevelGraphic(ax + 1, ay, gfx_wall_growing_right, 0);
9928
9929       new_wall = TRUE;
9930     }
9931   }
9932
9933   if (element == EL_EXPANDABLE_WALL && (free_left || free_right))
9934     TEST_DrawLevelField(ax, ay);
9935
9936   if (!IN_LEV_FIELD(ax, ay - 1) || IS_WALL(Tile[ax][ay - 1]))
9937     stop_top = TRUE;
9938   if (!IN_LEV_FIELD(ax, ay + 1) || IS_WALL(Tile[ax][ay + 1]))
9939     stop_bottom = TRUE;
9940   if (!IN_LEV_FIELD(ax - 1, ay) || IS_WALL(Tile[ax - 1][ay]))
9941     stop_left = TRUE;
9942   if (!IN_LEV_FIELD(ax + 1, ay) || IS_WALL(Tile[ax + 1][ay]))
9943     stop_right = TRUE;
9944
9945   if (((stop_top && stop_bottom) || stop_horizontal) &&
9946       ((stop_left && stop_right) || stop_vertical))
9947     Tile[ax][ay] = (is_steelwall ? EL_STEELWALL : EL_WALL);
9948
9949   if (new_wall)
9950     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9951 }
9952
9953 static void CheckForDragon(int x, int y)
9954 {
9955   int i, j;
9956   boolean dragon_found = FALSE;
9957   struct XY *xy = xy_topdown;
9958
9959   for (i = 0; i < NUM_DIRECTIONS; i++)
9960   {
9961     for (j = 0; j < 4; j++)
9962     {
9963       int xx = x + j * xy[i].x;
9964       int yy = y + j * xy[i].y;
9965
9966       if (IN_LEV_FIELD(xx, yy) &&
9967           (Tile[xx][yy] == EL_FLAMES || Tile[xx][yy] == EL_DRAGON))
9968       {
9969         if (Tile[xx][yy] == EL_DRAGON)
9970           dragon_found = TRUE;
9971       }
9972       else
9973         break;
9974     }
9975   }
9976
9977   if (!dragon_found)
9978   {
9979     for (i = 0; i < NUM_DIRECTIONS; i++)
9980     {
9981       for (j = 0; j < 3; j++)
9982       {
9983         int xx = x + j * xy[i].x;
9984         int yy = y + j * xy[i].y;
9985
9986         if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == EL_FLAMES)
9987         {
9988           Tile[xx][yy] = EL_EMPTY;
9989           TEST_DrawLevelField(xx, yy);
9990         }
9991         else
9992           break;
9993       }
9994     }
9995   }
9996 }
9997
9998 static void InitBuggyBase(int x, int y)
9999 {
10000   int element = Tile[x][y];
10001   int activating_delay = FRAMES_PER_SECOND / 4;
10002
10003   ChangeDelay[x][y] =
10004     (element == EL_SP_BUGGY_BASE ?
10005      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
10006      element == EL_SP_BUGGY_BASE_ACTIVATING ?
10007      activating_delay :
10008      element == EL_SP_BUGGY_BASE_ACTIVE ?
10009      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
10010 }
10011
10012 static void WarnBuggyBase(int x, int y)
10013 {
10014   int i;
10015   struct XY *xy = xy_topdown;
10016
10017   for (i = 0; i < NUM_DIRECTIONS; i++)
10018   {
10019     int xx = x + xy[i].x;
10020     int yy = y + xy[i].y;
10021
10022     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
10023     {
10024       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
10025
10026       break;
10027     }
10028   }
10029 }
10030
10031 static void InitTrap(int x, int y)
10032 {
10033   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
10034 }
10035
10036 static void ActivateTrap(int x, int y)
10037 {
10038   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
10039 }
10040
10041 static void ChangeActiveTrap(int x, int y)
10042 {
10043   int graphic = IMG_TRAP_ACTIVE;
10044
10045   // if new animation frame was drawn, correct crumbled sand border
10046   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
10047     TEST_DrawLevelFieldCrumbled(x, y);
10048 }
10049
10050 static int getSpecialActionElement(int element, int number, int base_element)
10051 {
10052   return (element != EL_EMPTY ? element :
10053           number != -1 ? base_element + number - 1 :
10054           EL_EMPTY);
10055 }
10056
10057 static int getModifiedActionNumber(int value_old, int operator, int operand,
10058                                    int value_min, int value_max)
10059 {
10060   int value_new = (operator == CA_MODE_SET      ? operand :
10061                    operator == CA_MODE_ADD      ? value_old + operand :
10062                    operator == CA_MODE_SUBTRACT ? value_old - operand :
10063                    operator == CA_MODE_MULTIPLY ? value_old * operand :
10064                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
10065                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
10066                    value_old);
10067
10068   return (value_new < value_min ? value_min :
10069           value_new > value_max ? value_max :
10070           value_new);
10071 }
10072
10073 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10074 {
10075   struct ElementInfo *ei = &element_info[element];
10076   struct ElementChangeInfo *change = &ei->change_page[page];
10077   int target_element = change->target_element;
10078   int action_type = change->action_type;
10079   int action_mode = change->action_mode;
10080   int action_arg = change->action_arg;
10081   int action_element = change->action_element;
10082   int i;
10083
10084   if (!change->has_action)
10085     return;
10086
10087   // ---------- determine action paramater values -----------------------------
10088
10089   int level_time_value =
10090     (level.time > 0 ? TimeLeft :
10091      TimePlayed);
10092
10093   int action_arg_element_raw =
10094     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
10095      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10096      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
10097      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
10098      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10099      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
10100      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
10101      EL_EMPTY);
10102   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10103
10104   int action_arg_direction =
10105     (action_arg >= CA_ARG_DIRECTION_LEFT &&
10106      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10107      action_arg == CA_ARG_DIRECTION_TRIGGER ?
10108      change->actual_trigger_side :
10109      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10110      MV_DIR_OPPOSITE(change->actual_trigger_side) :
10111      MV_NONE);
10112
10113   int action_arg_number_min =
10114     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10115      CA_ARG_MIN);
10116
10117   int action_arg_number_max =
10118     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10119      action_type == CA_SET_LEVEL_GEMS ? 999 :
10120      action_type == CA_SET_LEVEL_TIME ? 9999 :
10121      action_type == CA_SET_LEVEL_SCORE ? 99999 :
10122      action_type == CA_SET_CE_VALUE ? 9999 :
10123      action_type == CA_SET_CE_SCORE ? 9999 :
10124      CA_ARG_MAX);
10125
10126   int action_arg_number_reset =
10127     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10128      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10129      action_type == CA_SET_LEVEL_TIME ? level.time :
10130      action_type == CA_SET_LEVEL_SCORE ? 0 :
10131      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10132      action_type == CA_SET_CE_SCORE ? 0 :
10133      0);
10134
10135   int action_arg_number =
10136     (action_arg <= CA_ARG_MAX ? action_arg :
10137      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10138      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10139      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10140      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10141      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10142      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10143      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10144      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10145      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10146      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10147      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? game.gems_still_needed :
10148      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? game.score :
10149      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10150      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10151      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10152      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10153      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10154      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10155      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
10156      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10157      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
10158      -1);
10159
10160   int action_arg_number_old =
10161     (action_type == CA_SET_LEVEL_GEMS ? game.gems_still_needed :
10162      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10163      action_type == CA_SET_LEVEL_SCORE ? game.score :
10164      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10165      action_type == CA_SET_CE_SCORE ? ei->collect_score :
10166      0);
10167
10168   int action_arg_number_new =
10169     getModifiedActionNumber(action_arg_number_old,
10170                             action_mode, action_arg_number,
10171                             action_arg_number_min, action_arg_number_max);
10172
10173   int trigger_player_bits =
10174     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10175      change->actual_trigger_player_bits : change->trigger_player);
10176
10177   int action_arg_player_bits =
10178     (action_arg >= CA_ARG_PLAYER_1 &&
10179      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10180      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10181      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10182      PLAYER_BITS_ANY);
10183
10184   // ---------- execute action  -----------------------------------------------
10185
10186   switch (action_type)
10187   {
10188     case CA_NO_ACTION:
10189     {
10190       return;
10191     }
10192
10193     // ---------- level actions  ----------------------------------------------
10194
10195     case CA_RESTART_LEVEL:
10196     {
10197       game.restart_level = TRUE;
10198
10199       break;
10200     }
10201
10202     case CA_SHOW_ENVELOPE:
10203     {
10204       int element = getSpecialActionElement(action_arg_element,
10205                                             action_arg_number, EL_ENVELOPE_1);
10206
10207       if (IS_ENVELOPE(element))
10208         local_player->show_envelope = element;
10209
10210       break;
10211     }
10212
10213     case CA_SET_LEVEL_TIME:
10214     {
10215       if (level.time > 0)       // only modify limited time value
10216       {
10217         TimeLeft = action_arg_number_new;
10218
10219         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10220
10221         DisplayGameControlValues();
10222
10223         if (!TimeLeft && game.time_limit)
10224           for (i = 0; i < MAX_PLAYERS; i++)
10225             KillPlayer(&stored_player[i]);
10226       }
10227
10228       break;
10229     }
10230
10231     case CA_SET_LEVEL_SCORE:
10232     {
10233       game.score = action_arg_number_new;
10234
10235       game_panel_controls[GAME_PANEL_SCORE].value = game.score;
10236
10237       DisplayGameControlValues();
10238
10239       break;
10240     }
10241
10242     case CA_SET_LEVEL_GEMS:
10243     {
10244       game.gems_still_needed = action_arg_number_new;
10245
10246       game.snapshot.collected_item = TRUE;
10247
10248       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
10249
10250       DisplayGameControlValues();
10251
10252       break;
10253     }
10254
10255     case CA_SET_LEVEL_WIND:
10256     {
10257       game.wind_direction = action_arg_direction;
10258
10259       break;
10260     }
10261
10262     case CA_SET_LEVEL_RANDOM_SEED:
10263     {
10264       // ensure that setting a new random seed while playing is predictable
10265       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10266
10267       break;
10268     }
10269
10270     // ---------- player actions  ---------------------------------------------
10271
10272     case CA_MOVE_PLAYER:
10273     case CA_MOVE_PLAYER_NEW:
10274     {
10275       // automatically move to the next field in specified direction
10276       for (i = 0; i < MAX_PLAYERS; i++)
10277         if (trigger_player_bits & (1 << i))
10278           if (action_type == CA_MOVE_PLAYER ||
10279               stored_player[i].MovPos == 0)
10280             stored_player[i].programmed_action = action_arg_direction;
10281
10282       break;
10283     }
10284
10285     case CA_EXIT_PLAYER:
10286     {
10287       for (i = 0; i < MAX_PLAYERS; i++)
10288         if (action_arg_player_bits & (1 << i))
10289           ExitPlayer(&stored_player[i]);
10290
10291       if (game.players_still_needed == 0)
10292         LevelSolved();
10293
10294       break;
10295     }
10296
10297     case CA_KILL_PLAYER:
10298     {
10299       for (i = 0; i < MAX_PLAYERS; i++)
10300         if (action_arg_player_bits & (1 << i))
10301           KillPlayer(&stored_player[i]);
10302
10303       break;
10304     }
10305
10306     case CA_SET_PLAYER_KEYS:
10307     {
10308       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10309       int element = getSpecialActionElement(action_arg_element,
10310                                             action_arg_number, EL_KEY_1);
10311
10312       if (IS_KEY(element))
10313       {
10314         for (i = 0; i < MAX_PLAYERS; i++)
10315         {
10316           if (trigger_player_bits & (1 << i))
10317           {
10318             stored_player[i].key[KEY_NR(element)] = key_state;
10319
10320             DrawGameDoorValues();
10321           }
10322         }
10323       }
10324
10325       break;
10326     }
10327
10328     case CA_SET_PLAYER_SPEED:
10329     {
10330       for (i = 0; i < MAX_PLAYERS; i++)
10331       {
10332         if (trigger_player_bits & (1 << i))
10333         {
10334           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10335
10336           if (action_arg == CA_ARG_SPEED_FASTER &&
10337               stored_player[i].cannot_move)
10338           {
10339             action_arg_number = STEPSIZE_VERY_SLOW;
10340           }
10341           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10342                    action_arg == CA_ARG_SPEED_FASTER)
10343           {
10344             action_arg_number = 2;
10345             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10346                            CA_MODE_MULTIPLY);
10347           }
10348           else if (action_arg == CA_ARG_NUMBER_RESET)
10349           {
10350             action_arg_number = level.initial_player_stepsize[i];
10351           }
10352
10353           move_stepsize =
10354             getModifiedActionNumber(move_stepsize,
10355                                     action_mode,
10356                                     action_arg_number,
10357                                     action_arg_number_min,
10358                                     action_arg_number_max);
10359
10360           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10361         }
10362       }
10363
10364       break;
10365     }
10366
10367     case CA_SET_PLAYER_SHIELD:
10368     {
10369       for (i = 0; i < MAX_PLAYERS; i++)
10370       {
10371         if (trigger_player_bits & (1 << i))
10372         {
10373           if (action_arg == CA_ARG_SHIELD_OFF)
10374           {
10375             stored_player[i].shield_normal_time_left = 0;
10376             stored_player[i].shield_deadly_time_left = 0;
10377           }
10378           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10379           {
10380             stored_player[i].shield_normal_time_left = 999999;
10381           }
10382           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10383           {
10384             stored_player[i].shield_normal_time_left = 999999;
10385             stored_player[i].shield_deadly_time_left = 999999;
10386           }
10387         }
10388       }
10389
10390       break;
10391     }
10392
10393     case CA_SET_PLAYER_GRAVITY:
10394     {
10395       for (i = 0; i < MAX_PLAYERS; i++)
10396       {
10397         if (trigger_player_bits & (1 << i))
10398         {
10399           stored_player[i].gravity =
10400             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10401              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10402              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10403              stored_player[i].gravity);
10404         }
10405       }
10406
10407       break;
10408     }
10409
10410     case CA_SET_PLAYER_ARTWORK:
10411     {
10412       for (i = 0; i < MAX_PLAYERS; i++)
10413       {
10414         if (trigger_player_bits & (1 << i))
10415         {
10416           int artwork_element = action_arg_element;
10417
10418           if (action_arg == CA_ARG_ELEMENT_RESET)
10419             artwork_element =
10420               (level.use_artwork_element[i] ? level.artwork_element[i] :
10421                stored_player[i].element_nr);
10422
10423           if (stored_player[i].artwork_element != artwork_element)
10424             stored_player[i].Frame = 0;
10425
10426           stored_player[i].artwork_element = artwork_element;
10427
10428           SetPlayerWaiting(&stored_player[i], FALSE);
10429
10430           // set number of special actions for bored and sleeping animation
10431           stored_player[i].num_special_action_bored =
10432             get_num_special_action(artwork_element,
10433                                    ACTION_BORING_1, ACTION_BORING_LAST);
10434           stored_player[i].num_special_action_sleeping =
10435             get_num_special_action(artwork_element,
10436                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10437         }
10438       }
10439
10440       break;
10441     }
10442
10443     case CA_SET_PLAYER_INVENTORY:
10444     {
10445       for (i = 0; i < MAX_PLAYERS; i++)
10446       {
10447         struct PlayerInfo *player = &stored_player[i];
10448         int j, k;
10449
10450         if (trigger_player_bits & (1 << i))
10451         {
10452           int inventory_element = action_arg_element;
10453
10454           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10455               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10456               action_arg == CA_ARG_ELEMENT_ACTION)
10457           {
10458             int element = inventory_element;
10459             int collect_count = element_info[element].collect_count_initial;
10460
10461             if (!IS_CUSTOM_ELEMENT(element))
10462               collect_count = 1;
10463
10464             if (collect_count == 0)
10465               player->inventory_infinite_element = element;
10466             else
10467               for (k = 0; k < collect_count; k++)
10468                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10469                   player->inventory_element[player->inventory_size++] =
10470                     element;
10471           }
10472           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10473                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10474                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10475           {
10476             if (player->inventory_infinite_element != EL_UNDEFINED &&
10477                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10478                                      action_arg_element_raw))
10479               player->inventory_infinite_element = EL_UNDEFINED;
10480
10481             for (k = 0, j = 0; j < player->inventory_size; j++)
10482             {
10483               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10484                                         action_arg_element_raw))
10485                 player->inventory_element[k++] = player->inventory_element[j];
10486             }
10487
10488             player->inventory_size = k;
10489           }
10490           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10491           {
10492             if (player->inventory_size > 0)
10493             {
10494               for (j = 0; j < player->inventory_size - 1; j++)
10495                 player->inventory_element[j] = player->inventory_element[j + 1];
10496
10497               player->inventory_size--;
10498             }
10499           }
10500           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10501           {
10502             if (player->inventory_size > 0)
10503               player->inventory_size--;
10504           }
10505           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10506           {
10507             player->inventory_infinite_element = EL_UNDEFINED;
10508             player->inventory_size = 0;
10509           }
10510           else if (action_arg == CA_ARG_INVENTORY_RESET)
10511           {
10512             player->inventory_infinite_element = EL_UNDEFINED;
10513             player->inventory_size = 0;
10514
10515             if (level.use_initial_inventory[i])
10516             {
10517               for (j = 0; j < level.initial_inventory_size[i]; j++)
10518               {
10519                 int element = level.initial_inventory_content[i][j];
10520                 int collect_count = element_info[element].collect_count_initial;
10521
10522                 if (!IS_CUSTOM_ELEMENT(element))
10523                   collect_count = 1;
10524
10525                 if (collect_count == 0)
10526                   player->inventory_infinite_element = element;
10527                 else
10528                   for (k = 0; k < collect_count; k++)
10529                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10530                       player->inventory_element[player->inventory_size++] =
10531                         element;
10532               }
10533             }
10534           }
10535         }
10536       }
10537
10538       break;
10539     }
10540
10541     // ---------- CE actions  -------------------------------------------------
10542
10543     case CA_SET_CE_VALUE:
10544     {
10545       int last_ce_value = CustomValue[x][y];
10546
10547       CustomValue[x][y] = action_arg_number_new;
10548
10549       if (CustomValue[x][y] != last_ce_value)
10550       {
10551         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10552         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10553
10554         if (CustomValue[x][y] == 0)
10555         {
10556           // reset change counter (else CE_VALUE_GETS_ZERO would not work)
10557           ChangeCount[x][y] = 0;        // allow at least one more change
10558
10559           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10560           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10561         }
10562       }
10563
10564       break;
10565     }
10566
10567     case CA_SET_CE_SCORE:
10568     {
10569       int last_ce_score = ei->collect_score;
10570
10571       ei->collect_score = action_arg_number_new;
10572
10573       if (ei->collect_score != last_ce_score)
10574       {
10575         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10576         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10577
10578         if (ei->collect_score == 0)
10579         {
10580           int xx, yy;
10581
10582           // reset change counter (else CE_SCORE_GETS_ZERO would not work)
10583           ChangeCount[x][y] = 0;        // allow at least one more change
10584
10585           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10586           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10587
10588           /*
10589             This is a very special case that seems to be a mixture between
10590             CheckElementChange() and CheckTriggeredElementChange(): while
10591             the first one only affects single elements that are triggered
10592             directly, the second one affects multiple elements in the playfield
10593             that are triggered indirectly by another element. This is a third
10594             case: Changing the CE score always affects multiple identical CEs,
10595             so every affected CE must be checked, not only the single CE for
10596             which the CE score was changed in the first place (as every instance
10597             of that CE shares the same CE score, and therefore also can change)!
10598           */
10599           SCAN_PLAYFIELD(xx, yy)
10600           {
10601             if (Tile[xx][yy] == element)
10602               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10603                                  CE_SCORE_GETS_ZERO);
10604           }
10605         }
10606       }
10607
10608       break;
10609     }
10610
10611     case CA_SET_CE_ARTWORK:
10612     {
10613       int artwork_element = action_arg_element;
10614       boolean reset_frame = FALSE;
10615       int xx, yy;
10616
10617       if (action_arg == CA_ARG_ELEMENT_RESET)
10618         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10619                            element);
10620
10621       if (ei->gfx_element != artwork_element)
10622         reset_frame = TRUE;
10623
10624       ei->gfx_element = artwork_element;
10625
10626       SCAN_PLAYFIELD(xx, yy)
10627       {
10628         if (Tile[xx][yy] == element)
10629         {
10630           if (reset_frame)
10631           {
10632             ResetGfxAnimation(xx, yy);
10633             ResetRandomAnimationValue(xx, yy);
10634           }
10635
10636           TEST_DrawLevelField(xx, yy);
10637         }
10638       }
10639
10640       break;
10641     }
10642
10643     // ---------- engine actions  ---------------------------------------------
10644
10645     case CA_SET_ENGINE_SCAN_MODE:
10646     {
10647       InitPlayfieldScanMode(action_arg);
10648
10649       break;
10650     }
10651
10652     default:
10653       break;
10654   }
10655 }
10656
10657 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10658 {
10659   int old_element = Tile[x][y];
10660   int new_element = GetElementFromGroupElement(element);
10661   int previous_move_direction = MovDir[x][y];
10662   int last_ce_value = CustomValue[x][y];
10663   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10664   boolean new_element_is_player = IS_PLAYER_ELEMENT(new_element);
10665   boolean add_player_onto_element = (new_element_is_player &&
10666                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10667                                      IS_WALKABLE(old_element));
10668
10669   if (!add_player_onto_element)
10670   {
10671     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10672       RemoveMovingField(x, y);
10673     else
10674       RemoveField(x, y);
10675
10676     Tile[x][y] = new_element;
10677
10678     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10679       MovDir[x][y] = previous_move_direction;
10680
10681     if (element_info[new_element].use_last_ce_value)
10682       CustomValue[x][y] = last_ce_value;
10683
10684     InitField_WithBug1(x, y, FALSE);
10685
10686     new_element = Tile[x][y];   // element may have changed
10687
10688     ResetGfxAnimation(x, y);
10689     ResetRandomAnimationValue(x, y);
10690
10691     TEST_DrawLevelField(x, y);
10692
10693     if (GFX_CRUMBLED(new_element))
10694       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10695
10696     if (old_element == EL_EXPLOSION)
10697     {
10698       Store[x][y] = Store2[x][y] = 0;
10699
10700       // check if new element replaces an exploding player, requiring cleanup
10701       if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
10702         StorePlayer[x][y] = 0;
10703     }
10704
10705     // check if element under the player changes from accessible to unaccessible
10706     // (needed for special case of dropping element which then changes)
10707     // (must be checked after creating new element for walkable group elements)
10708     if (IS_PLAYER(x, y) && !player_explosion_protected &&
10709         IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10710     {
10711       KillPlayer(PLAYERINFO(x, y));
10712
10713       return;
10714     }
10715   }
10716
10717   // "ChangeCount" not set yet to allow "entered by player" change one time
10718   if (new_element_is_player)
10719     RelocatePlayer(x, y, new_element);
10720
10721   if (is_change)
10722     ChangeCount[x][y]++;        // count number of changes in the same frame
10723
10724   TestIfBadThingTouchesPlayer(x, y);
10725   TestIfPlayerTouchesCustomElement(x, y);
10726   TestIfElementTouchesCustomElement(x, y);
10727 }
10728
10729 static void CreateField(int x, int y, int element)
10730 {
10731   CreateFieldExt(x, y, element, FALSE);
10732 }
10733
10734 static void CreateElementFromChange(int x, int y, int element)
10735 {
10736   element = GET_VALID_RUNTIME_ELEMENT(element);
10737
10738   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10739   {
10740     int old_element = Tile[x][y];
10741
10742     // prevent changed element from moving in same engine frame
10743     // unless both old and new element can either fall or move
10744     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10745         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10746       Stop[x][y] = TRUE;
10747   }
10748
10749   CreateFieldExt(x, y, element, TRUE);
10750 }
10751
10752 static boolean ChangeElement(int x, int y, int element, int page)
10753 {
10754   struct ElementInfo *ei = &element_info[element];
10755   struct ElementChangeInfo *change = &ei->change_page[page];
10756   int ce_value = CustomValue[x][y];
10757   int ce_score = ei->collect_score;
10758   int target_element;
10759   int old_element = Tile[x][y];
10760
10761   // always use default change event to prevent running into a loop
10762   if (ChangeEvent[x][y] == -1)
10763     ChangeEvent[x][y] = CE_DELAY;
10764
10765   if (ChangeEvent[x][y] == CE_DELAY)
10766   {
10767     // reset actual trigger element, trigger player and action element
10768     change->actual_trigger_element = EL_EMPTY;
10769     change->actual_trigger_player = EL_EMPTY;
10770     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10771     change->actual_trigger_side = CH_SIDE_NONE;
10772     change->actual_trigger_ce_value = 0;
10773     change->actual_trigger_ce_score = 0;
10774     change->actual_trigger_x = -1;
10775     change->actual_trigger_y = -1;
10776   }
10777
10778   // do not change elements more than a specified maximum number of changes
10779   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10780     return FALSE;
10781
10782   ChangeCount[x][y]++;          // count number of changes in the same frame
10783
10784   if (ei->has_anim_event)
10785     HandleGlobalAnimEventByElementChange(element, page, x, y,
10786                                          change->actual_trigger_x,
10787                                          change->actual_trigger_y);
10788
10789   if (change->explode)
10790   {
10791     Bang(x, y);
10792
10793     return TRUE;
10794   }
10795
10796   if (change->use_target_content)
10797   {
10798     boolean complete_replace = TRUE;
10799     boolean can_replace[3][3];
10800     int xx, yy;
10801
10802     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10803     {
10804       boolean is_empty;
10805       boolean is_walkable;
10806       boolean is_diggable;
10807       boolean is_collectible;
10808       boolean is_removable;
10809       boolean is_destructible;
10810       int ex = x + xx - 1;
10811       int ey = y + yy - 1;
10812       int content_element = change->target_content.e[xx][yy];
10813       int e;
10814
10815       can_replace[xx][yy] = TRUE;
10816
10817       if (ex == x && ey == y)   // do not check changing element itself
10818         continue;
10819
10820       if (content_element == EL_EMPTY_SPACE)
10821       {
10822         can_replace[xx][yy] = FALSE;    // do not replace border with space
10823
10824         continue;
10825       }
10826
10827       if (!IN_LEV_FIELD(ex, ey))
10828       {
10829         can_replace[xx][yy] = FALSE;
10830         complete_replace = FALSE;
10831
10832         continue;
10833       }
10834
10835       e = Tile[ex][ey];
10836
10837       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10838         e = MovingOrBlocked2Element(ex, ey);
10839
10840       is_empty = (IS_FREE(ex, ey) ||
10841                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10842
10843       is_walkable     = (is_empty || IS_WALKABLE(e));
10844       is_diggable     = (is_empty || IS_DIGGABLE(e));
10845       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10846       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10847       is_removable    = (is_diggable || is_collectible);
10848
10849       can_replace[xx][yy] =
10850         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10851           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10852           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10853           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10854           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10855           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10856          !(IS_PLAYER(ex, ey) && IS_PLAYER_ELEMENT(content_element)));
10857
10858       if (!can_replace[xx][yy])
10859         complete_replace = FALSE;
10860     }
10861
10862     if (!change->only_if_complete || complete_replace)
10863     {
10864       boolean something_has_changed = FALSE;
10865
10866       if (change->only_if_complete && change->use_random_replace &&
10867           RND(100) < change->random_percentage)
10868         return FALSE;
10869
10870       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10871       {
10872         int ex = x + xx - 1;
10873         int ey = y + yy - 1;
10874         int content_element;
10875
10876         if (can_replace[xx][yy] && (!change->use_random_replace ||
10877                                     RND(100) < change->random_percentage))
10878         {
10879           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10880             RemoveMovingField(ex, ey);
10881
10882           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10883
10884           content_element = change->target_content.e[xx][yy];
10885           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10886                                               ce_value, ce_score);
10887
10888           CreateElementFromChange(ex, ey, target_element);
10889
10890           something_has_changed = TRUE;
10891
10892           // for symmetry reasons, freeze newly created border elements
10893           if (ex != x || ey != y)
10894             Stop[ex][ey] = TRUE;        // no more moving in this frame
10895         }
10896       }
10897
10898       if (something_has_changed)
10899       {
10900         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10901         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10902       }
10903     }
10904   }
10905   else
10906   {
10907     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10908                                         ce_value, ce_score);
10909
10910     if (element == EL_DIAGONAL_GROWING ||
10911         element == EL_DIAGONAL_SHRINKING)
10912     {
10913       target_element = Store[x][y];
10914
10915       Store[x][y] = EL_EMPTY;
10916     }
10917
10918     // special case: element changes to player (and may be kept if walkable)
10919     if (IS_PLAYER_ELEMENT(target_element) && !level.keep_walkable_ce)
10920       CreateElementFromChange(x, y, EL_EMPTY);
10921
10922     CreateElementFromChange(x, y, target_element);
10923
10924     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10925     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10926   }
10927
10928   // this uses direct change before indirect change
10929   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10930
10931   return TRUE;
10932 }
10933
10934 static void HandleElementChange(int x, int y, int page)
10935 {
10936   int element = MovingOrBlocked2Element(x, y);
10937   struct ElementInfo *ei = &element_info[element];
10938   struct ElementChangeInfo *change = &ei->change_page[page];
10939   boolean handle_action_before_change = FALSE;
10940
10941 #ifdef DEBUG
10942   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10943       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10944   {
10945     Debug("game:playing:HandleElementChange", "%d,%d: element = %d ('%s')",
10946           x, y, element, element_info[element].token_name);
10947     Debug("game:playing:HandleElementChange", "This should never happen!");
10948   }
10949 #endif
10950
10951   // this can happen with classic bombs on walkable, changing elements
10952   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10953   {
10954     return;
10955   }
10956
10957   if (ChangeDelay[x][y] == 0)           // initialize element change
10958   {
10959     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10960
10961     if (change->can_change)
10962     {
10963       // !!! not clear why graphic animation should be reset at all here !!!
10964       // !!! UPDATE: but is needed for correct Snake Bite tail animation !!!
10965       // !!! SOLUTION: do not reset if graphics engine set to 4 or above !!!
10966
10967       /*
10968         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10969
10970         When using an animation frame delay of 1 (this only happens with
10971         "sp_zonk.moving.left/right" in the classic graphics), the default
10972         (non-moving) animation shows wrong animation frames (while the
10973         moving animation, like "sp_zonk.moving.left/right", is correct,
10974         so this graphical bug never shows up with the classic graphics).
10975         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10976         be drawn instead of the correct frames 0,1,2,3. This is caused by
10977         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10978         an element change: First when the change delay ("ChangeDelay[][]")
10979         counter has reached zero after decrementing, then a second time in
10980         the next frame (after "GfxFrame[][]" was already incremented) when
10981         "ChangeDelay[][]" is reset to the initial delay value again.
10982
10983         This causes frame 0 to be drawn twice, while the last frame won't
10984         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10985
10986         As some animations may already be cleverly designed around this bug
10987         (at least the "Snake Bite" snake tail animation does this), it cannot
10988         simply be fixed here without breaking such existing animations.
10989         Unfortunately, it cannot easily be detected if a graphics set was
10990         designed "before" or "after" the bug was fixed. As a workaround,
10991         a new graphics set option "game.graphics_engine_version" was added
10992         to be able to specify the game's major release version for which the
10993         graphics set was designed, which can then be used to decide if the
10994         bugfix should be used (version 4 and above) or not (version 3 or
10995         below, or if no version was specified at all, as with old sets).
10996
10997         (The wrong/fixed animation frames can be tested with the test level set
10998         "test_gfxframe" and level "000", which contains a specially prepared
10999         custom element at level position (x/y) == (11/9) which uses the zonk
11000         animation mentioned above. Using "game.graphics_engine_version: 4"
11001         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
11002         This can also be seen from the debug output for this test element.)
11003       */
11004
11005       // when a custom element is about to change (for example by change delay),
11006       // do not reset graphic animation when the custom element is moving
11007       if (game.graphics_engine_version < 4 &&
11008           !IS_MOVING(x, y))
11009       {
11010         ResetGfxAnimation(x, y);
11011         ResetRandomAnimationValue(x, y);
11012       }
11013
11014       if (change->pre_change_function)
11015         change->pre_change_function(x, y);
11016     }
11017   }
11018
11019   ChangeDelay[x][y]--;
11020
11021   if (ChangeDelay[x][y] != 0)           // continue element change
11022   {
11023     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11024
11025     // also needed if CE can not change, but has CE delay with CE action
11026     if (IS_ANIMATED(graphic))
11027       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11028
11029     if (change->can_change)
11030     {
11031       if (change->change_function)
11032         change->change_function(x, y);
11033     }
11034   }
11035   else                                  // finish element change
11036   {
11037     if (ChangePage[x][y] != -1)         // remember page from delayed change
11038     {
11039       page = ChangePage[x][y];
11040       ChangePage[x][y] = -1;
11041
11042       change = &ei->change_page[page];
11043     }
11044
11045     if (IS_MOVING(x, y))                // never change a running system ;-)
11046     {
11047       ChangeDelay[x][y] = 1;            // try change after next move step
11048       ChangePage[x][y] = page;          // remember page to use for change
11049
11050       return;
11051     }
11052
11053     // special case: set new level random seed before changing element
11054     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
11055       handle_action_before_change = TRUE;
11056
11057     if (change->has_action && handle_action_before_change)
11058       ExecuteCustomElementAction(x, y, element, page);
11059
11060     if (change->can_change)
11061     {
11062       if (ChangeElement(x, y, element, page))
11063       {
11064         if (change->post_change_function)
11065           change->post_change_function(x, y);
11066       }
11067     }
11068
11069     if (change->has_action && !handle_action_before_change)
11070       ExecuteCustomElementAction(x, y, element, page);
11071   }
11072 }
11073
11074 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11075                                               int trigger_element,
11076                                               int trigger_event,
11077                                               int trigger_player,
11078                                               int trigger_side,
11079                                               int trigger_page)
11080 {
11081   boolean change_done_any = FALSE;
11082   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11083   int i;
11084
11085   if (!(trigger_events[trigger_element][trigger_event]))
11086     return FALSE;
11087
11088   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11089
11090   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11091   {
11092     int element = EL_CUSTOM_START + i;
11093     boolean change_done = FALSE;
11094     int p;
11095
11096     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11097         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11098       continue;
11099
11100     for (p = 0; p < element_info[element].num_change_pages; p++)
11101     {
11102       struct ElementChangeInfo *change = &element_info[element].change_page[p];
11103
11104       if (change->can_change_or_has_action &&
11105           change->has_event[trigger_event] &&
11106           change->trigger_side & trigger_side &&
11107           change->trigger_player & trigger_player &&
11108           change->trigger_page & trigger_page_bits &&
11109           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11110       {
11111         change->actual_trigger_element = trigger_element;
11112         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11113         change->actual_trigger_player_bits = trigger_player;
11114         change->actual_trigger_side = trigger_side;
11115         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11116         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11117         change->actual_trigger_x = trigger_x;
11118         change->actual_trigger_y = trigger_y;
11119
11120         if ((change->can_change && !change_done) || change->has_action)
11121         {
11122           int x, y;
11123
11124           SCAN_PLAYFIELD(x, y)
11125           {
11126             if (Tile[x][y] == element)
11127             {
11128               if (change->can_change && !change_done)
11129               {
11130                 // if element already changed in this frame, not only prevent
11131                 // another element change (checked in ChangeElement()), but
11132                 // also prevent additional element actions for this element
11133
11134                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11135                     !level.use_action_after_change_bug)
11136                   continue;
11137
11138                 ChangeDelay[x][y] = 1;
11139                 ChangeEvent[x][y] = trigger_event;
11140
11141                 HandleElementChange(x, y, p);
11142               }
11143               else if (change->has_action)
11144               {
11145                 // if element already changed in this frame, not only prevent
11146                 // another element change (checked in ChangeElement()), but
11147                 // also prevent additional element actions for this element
11148
11149                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11150                     !level.use_action_after_change_bug)
11151                   continue;
11152
11153                 ExecuteCustomElementAction(x, y, element, p);
11154                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11155               }
11156             }
11157           }
11158
11159           if (change->can_change)
11160           {
11161             change_done = TRUE;
11162             change_done_any = TRUE;
11163           }
11164         }
11165       }
11166     }
11167   }
11168
11169   RECURSION_LOOP_DETECTION_END();
11170
11171   return change_done_any;
11172 }
11173
11174 static boolean CheckElementChangeExt(int x, int y,
11175                                      int element,
11176                                      int trigger_element,
11177                                      int trigger_event,
11178                                      int trigger_player,
11179                                      int trigger_side)
11180 {
11181   boolean change_done = FALSE;
11182   int p;
11183
11184   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11185       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11186     return FALSE;
11187
11188   if (Tile[x][y] == EL_BLOCKED)
11189   {
11190     Blocked2Moving(x, y, &x, &y);
11191     element = Tile[x][y];
11192   }
11193
11194   // check if element has already changed or is about to change after moving
11195   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11196        Tile[x][y] != element) ||
11197
11198       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11199        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11200         ChangePage[x][y] != -1)))
11201     return FALSE;
11202
11203   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11204
11205   for (p = 0; p < element_info[element].num_change_pages; p++)
11206   {
11207     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11208
11209     /* check trigger element for all events where the element that is checked
11210        for changing interacts with a directly adjacent element -- this is
11211        different to element changes that affect other elements to change on the
11212        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11213     boolean check_trigger_element =
11214       (trigger_event == CE_NEXT_TO_X ||
11215        trigger_event == CE_TOUCHING_X ||
11216        trigger_event == CE_HITTING_X ||
11217        trigger_event == CE_HIT_BY_X ||
11218        trigger_event == CE_DIGGING_X); // this one was forgotten until 3.2.3
11219
11220     if (change->can_change_or_has_action &&
11221         change->has_event[trigger_event] &&
11222         change->trigger_side & trigger_side &&
11223         change->trigger_player & trigger_player &&
11224         (!check_trigger_element ||
11225          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11226     {
11227       change->actual_trigger_element = trigger_element;
11228       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11229       change->actual_trigger_player_bits = trigger_player;
11230       change->actual_trigger_side = trigger_side;
11231       change->actual_trigger_ce_value = CustomValue[x][y];
11232       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11233       change->actual_trigger_x = x;
11234       change->actual_trigger_y = y;
11235
11236       // special case: trigger element not at (x,y) position for some events
11237       if (check_trigger_element)
11238       {
11239         static struct
11240         {
11241           int dx, dy;
11242         } move_xy[] =
11243           {
11244             {  0,  0 },
11245             { -1,  0 },
11246             { +1,  0 },
11247             {  0,  0 },
11248             {  0, -1 },
11249             {  0,  0 }, { 0, 0 }, { 0, 0 },
11250             {  0, +1 }
11251           };
11252
11253         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11254         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11255
11256         change->actual_trigger_ce_value = CustomValue[xx][yy];
11257         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11258         change->actual_trigger_x = xx;
11259         change->actual_trigger_y = yy;
11260       }
11261
11262       if (change->can_change && !change_done)
11263       {
11264         ChangeDelay[x][y] = 1;
11265         ChangeEvent[x][y] = trigger_event;
11266
11267         HandleElementChange(x, y, p);
11268
11269         change_done = TRUE;
11270       }
11271       else if (change->has_action)
11272       {
11273         ExecuteCustomElementAction(x, y, element, p);
11274         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11275       }
11276     }
11277   }
11278
11279   RECURSION_LOOP_DETECTION_END();
11280
11281   return change_done;
11282 }
11283
11284 static void PlayPlayerSound(struct PlayerInfo *player)
11285 {
11286   int jx = player->jx, jy = player->jy;
11287   int sound_element = player->artwork_element;
11288   int last_action = player->last_action_waiting;
11289   int action = player->action_waiting;
11290
11291   if (player->is_waiting)
11292   {
11293     if (action != last_action)
11294       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11295     else
11296       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11297   }
11298   else
11299   {
11300     if (action != last_action)
11301       StopSound(element_info[sound_element].sound[last_action]);
11302
11303     if (last_action == ACTION_SLEEPING)
11304       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11305   }
11306 }
11307
11308 static void PlayAllPlayersSound(void)
11309 {
11310   int i;
11311
11312   for (i = 0; i < MAX_PLAYERS; i++)
11313     if (stored_player[i].active)
11314       PlayPlayerSound(&stored_player[i]);
11315 }
11316
11317 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11318 {
11319   boolean last_waiting = player->is_waiting;
11320   int move_dir = player->MovDir;
11321
11322   player->dir_waiting = move_dir;
11323   player->last_action_waiting = player->action_waiting;
11324
11325   if (is_waiting)
11326   {
11327     if (!last_waiting)          // not waiting -> waiting
11328     {
11329       player->is_waiting = TRUE;
11330
11331       player->frame_counter_bored =
11332         FrameCounter +
11333         game.player_boring_delay_fixed +
11334         GetSimpleRandom(game.player_boring_delay_random);
11335       player->frame_counter_sleeping =
11336         FrameCounter +
11337         game.player_sleeping_delay_fixed +
11338         GetSimpleRandom(game.player_sleeping_delay_random);
11339
11340       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11341     }
11342
11343     if (game.player_sleeping_delay_fixed +
11344         game.player_sleeping_delay_random > 0 &&
11345         player->anim_delay_counter == 0 &&
11346         player->post_delay_counter == 0 &&
11347         FrameCounter >= player->frame_counter_sleeping)
11348       player->is_sleeping = TRUE;
11349     else if (game.player_boring_delay_fixed +
11350              game.player_boring_delay_random > 0 &&
11351              FrameCounter >= player->frame_counter_bored)
11352       player->is_bored = TRUE;
11353
11354     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11355                               player->is_bored ? ACTION_BORING :
11356                               ACTION_WAITING);
11357
11358     if (player->is_sleeping && player->use_murphy)
11359     {
11360       // special case for sleeping Murphy when leaning against non-free tile
11361
11362       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11363           (Tile[player->jx - 1][player->jy] != EL_EMPTY &&
11364            !IS_MOVING(player->jx - 1, player->jy)))
11365         move_dir = MV_LEFT;
11366       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11367                (Tile[player->jx + 1][player->jy] != EL_EMPTY &&
11368                 !IS_MOVING(player->jx + 1, player->jy)))
11369         move_dir = MV_RIGHT;
11370       else
11371         player->is_sleeping = FALSE;
11372
11373       player->dir_waiting = move_dir;
11374     }
11375
11376     if (player->is_sleeping)
11377     {
11378       if (player->num_special_action_sleeping > 0)
11379       {
11380         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11381         {
11382           int last_special_action = player->special_action_sleeping;
11383           int num_special_action = player->num_special_action_sleeping;
11384           int special_action =
11385             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11386              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11387              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11388              last_special_action + 1 : ACTION_SLEEPING);
11389           int special_graphic =
11390             el_act_dir2img(player->artwork_element, special_action, move_dir);
11391
11392           player->anim_delay_counter =
11393             graphic_info[special_graphic].anim_delay_fixed +
11394             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11395           player->post_delay_counter =
11396             graphic_info[special_graphic].post_delay_fixed +
11397             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11398
11399           player->special_action_sleeping = special_action;
11400         }
11401
11402         if (player->anim_delay_counter > 0)
11403         {
11404           player->action_waiting = player->special_action_sleeping;
11405           player->anim_delay_counter--;
11406         }
11407         else if (player->post_delay_counter > 0)
11408         {
11409           player->post_delay_counter--;
11410         }
11411       }
11412     }
11413     else if (player->is_bored)
11414     {
11415       if (player->num_special_action_bored > 0)
11416       {
11417         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11418         {
11419           int special_action =
11420             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11421           int special_graphic =
11422             el_act_dir2img(player->artwork_element, special_action, move_dir);
11423
11424           player->anim_delay_counter =
11425             graphic_info[special_graphic].anim_delay_fixed +
11426             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11427           player->post_delay_counter =
11428             graphic_info[special_graphic].post_delay_fixed +
11429             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11430
11431           player->special_action_bored = special_action;
11432         }
11433
11434         if (player->anim_delay_counter > 0)
11435         {
11436           player->action_waiting = player->special_action_bored;
11437           player->anim_delay_counter--;
11438         }
11439         else if (player->post_delay_counter > 0)
11440         {
11441           player->post_delay_counter--;
11442         }
11443       }
11444     }
11445   }
11446   else if (last_waiting)        // waiting -> not waiting
11447   {
11448     player->is_waiting = FALSE;
11449     player->is_bored = FALSE;
11450     player->is_sleeping = FALSE;
11451
11452     player->frame_counter_bored = -1;
11453     player->frame_counter_sleeping = -1;
11454
11455     player->anim_delay_counter = 0;
11456     player->post_delay_counter = 0;
11457
11458     player->dir_waiting = player->MovDir;
11459     player->action_waiting = ACTION_DEFAULT;
11460
11461     player->special_action_bored = ACTION_DEFAULT;
11462     player->special_action_sleeping = ACTION_DEFAULT;
11463   }
11464 }
11465
11466 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
11467 {
11468   if ((!player->is_moving  && player->was_moving) ||
11469       (player->MovPos == 0 && player->was_moving) ||
11470       (player->is_snapping && !player->was_snapping) ||
11471       (player->is_dropping && !player->was_dropping))
11472   {
11473     if (!CheckSaveEngineSnapshotToList())
11474       return;
11475
11476     player->was_moving = FALSE;
11477     player->was_snapping = TRUE;
11478     player->was_dropping = TRUE;
11479   }
11480   else
11481   {
11482     if (player->is_moving)
11483       player->was_moving = TRUE;
11484
11485     if (!player->is_snapping)
11486       player->was_snapping = FALSE;
11487
11488     if (!player->is_dropping)
11489       player->was_dropping = FALSE;
11490   }
11491
11492   static struct MouseActionInfo mouse_action_last = { 0 };
11493   struct MouseActionInfo mouse_action = player->effective_mouse_action;
11494   boolean new_released = (!mouse_action.button && mouse_action_last.button);
11495
11496   if (new_released)
11497     CheckSaveEngineSnapshotToList();
11498
11499   mouse_action_last = mouse_action;
11500 }
11501
11502 static void CheckSingleStepMode(struct PlayerInfo *player)
11503 {
11504   if (tape.single_step && tape.recording && !tape.pausing)
11505   {
11506     // as it is called "single step mode", just return to pause mode when the
11507     // player stopped moving after one tile (or never starts moving at all)
11508     // (reverse logic needed here in case single step mode used in team mode)
11509     if (player->is_moving ||
11510         player->is_pushing ||
11511         player->is_dropping_pressed ||
11512         player->effective_mouse_action.button)
11513       game.enter_single_step_mode = FALSE;
11514   }
11515
11516   CheckSaveEngineSnapshot(player);
11517 }
11518
11519 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11520 {
11521   int left      = player_action & JOY_LEFT;
11522   int right     = player_action & JOY_RIGHT;
11523   int up        = player_action & JOY_UP;
11524   int down      = player_action & JOY_DOWN;
11525   int button1   = player_action & JOY_BUTTON_1;
11526   int button2   = player_action & JOY_BUTTON_2;
11527   int dx        = (left ? -1 : right ? 1 : 0);
11528   int dy        = (up   ? -1 : down  ? 1 : 0);
11529
11530   if (!player->active || tape.pausing)
11531     return 0;
11532
11533   if (player_action)
11534   {
11535     if (button1)
11536       SnapField(player, dx, dy);
11537     else
11538     {
11539       if (button2)
11540         DropElement(player);
11541
11542       MovePlayer(player, dx, dy);
11543     }
11544
11545     CheckSingleStepMode(player);
11546
11547     SetPlayerWaiting(player, FALSE);
11548
11549     return player_action;
11550   }
11551   else
11552   {
11553     // no actions for this player (no input at player's configured device)
11554
11555     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11556     SnapField(player, 0, 0);
11557     CheckGravityMovementWhenNotMoving(player);
11558
11559     if (player->MovPos == 0)
11560       SetPlayerWaiting(player, TRUE);
11561
11562     if (player->MovPos == 0)    // needed for tape.playing
11563       player->is_moving = FALSE;
11564
11565     player->is_dropping = FALSE;
11566     player->is_dropping_pressed = FALSE;
11567     player->drop_pressed_delay = 0;
11568
11569     CheckSingleStepMode(player);
11570
11571     return 0;
11572   }
11573 }
11574
11575 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11576                                          byte *tape_action)
11577 {
11578   if (!tape.use_mouse_actions)
11579     return;
11580
11581   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11582   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11583   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11584 }
11585
11586 static void SetTapeActionFromMouseAction(byte *tape_action,
11587                                          struct MouseActionInfo *mouse_action)
11588 {
11589   if (!tape.use_mouse_actions)
11590     return;
11591
11592   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11593   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11594   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11595 }
11596
11597 static void CheckLevelSolved(void)
11598 {
11599   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11600   {
11601     if (game_em.level_solved &&
11602         !game_em.game_over)                             // game won
11603     {
11604       LevelSolved();
11605
11606       game_em.game_over = TRUE;
11607
11608       game.all_players_gone = TRUE;
11609     }
11610
11611     if (game_em.game_over)                              // game lost
11612       game.all_players_gone = TRUE;
11613   }
11614   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11615   {
11616     if (game_sp.level_solved &&
11617         !game_sp.game_over)                             // game won
11618     {
11619       LevelSolved();
11620
11621       game_sp.game_over = TRUE;
11622
11623       game.all_players_gone = TRUE;
11624     }
11625
11626     if (game_sp.game_over)                              // game lost
11627       game.all_players_gone = TRUE;
11628   }
11629   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11630   {
11631     if (game_mm.level_solved &&
11632         !game_mm.game_over)                             // game won
11633     {
11634       LevelSolved();
11635
11636       game_mm.game_over = TRUE;
11637
11638       game.all_players_gone = TRUE;
11639     }
11640
11641     if (game_mm.game_over)                              // game lost
11642       game.all_players_gone = TRUE;
11643   }
11644 }
11645
11646 static void CheckLevelTime_StepCounter(void)
11647 {
11648   int i;
11649
11650   TimePlayed++;
11651
11652   if (TimeLeft > 0)
11653   {
11654     TimeLeft--;
11655
11656     if (TimeLeft <= 10 && game.time_limit && !game.LevelSolved)
11657       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11658
11659     game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11660
11661     DisplayGameControlValues();
11662
11663     if (!TimeLeft && game.time_limit && !game.LevelSolved)
11664       for (i = 0; i < MAX_PLAYERS; i++)
11665         KillPlayer(&stored_player[i]);
11666   }
11667   else if (game.no_level_time_limit && !game.all_players_gone)
11668   {
11669     game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11670
11671     DisplayGameControlValues();
11672   }
11673 }
11674
11675 static void CheckLevelTime(void)
11676 {
11677   int i;
11678
11679   if (TimeFrames >= FRAMES_PER_SECOND)
11680   {
11681     TimeFrames = 0;
11682     TapeTime++;
11683
11684     for (i = 0; i < MAX_PLAYERS; i++)
11685     {
11686       struct PlayerInfo *player = &stored_player[i];
11687
11688       if (SHIELD_ON(player))
11689       {
11690         player->shield_normal_time_left--;
11691
11692         if (player->shield_deadly_time_left > 0)
11693           player->shield_deadly_time_left--;
11694       }
11695     }
11696
11697     if (!game.LevelSolved && !level.use_step_counter)
11698     {
11699       TimePlayed++;
11700
11701       if (TimeLeft > 0)
11702       {
11703         TimeLeft--;
11704
11705         if (TimeLeft <= 10 && game.time_limit)
11706           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11707
11708         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11709            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11710
11711         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11712
11713         if (!TimeLeft && game.time_limit)
11714         {
11715           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11716             game_em.lev->killed_out_of_time = TRUE;
11717           else
11718             for (i = 0; i < MAX_PLAYERS; i++)
11719               KillPlayer(&stored_player[i]);
11720         }
11721       }
11722       else if (game.no_level_time_limit && !game.all_players_gone)
11723       {
11724         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11725       }
11726
11727       game_em.lev->time = (game.no_level_time_limit ? TimePlayed : TimeLeft);
11728     }
11729
11730     if (tape.recording || tape.playing)
11731       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11732   }
11733
11734   if (tape.recording || tape.playing)
11735     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11736
11737   UpdateAndDisplayGameControlValues();
11738 }
11739
11740 void AdvanceFrameAndPlayerCounters(int player_nr)
11741 {
11742   int i;
11743
11744   // advance frame counters (global frame counter and time frame counter)
11745   FrameCounter++;
11746   TimeFrames++;
11747
11748   // advance player counters (counters for move delay, move animation etc.)
11749   for (i = 0; i < MAX_PLAYERS; i++)
11750   {
11751     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11752     int move_delay_value = stored_player[i].move_delay_value;
11753     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11754
11755     if (!advance_player_counters)       // not all players may be affected
11756       continue;
11757
11758     if (move_frames == 0)       // less than one move per game frame
11759     {
11760       int stepsize = TILEX / move_delay_value;
11761       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11762       int count = (stored_player[i].is_moving ?
11763                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11764
11765       if (count % delay == 0)
11766         move_frames = 1;
11767     }
11768
11769     stored_player[i].Frame += move_frames;
11770
11771     if (stored_player[i].MovPos != 0)
11772       stored_player[i].StepFrame += move_frames;
11773
11774     if (stored_player[i].move_delay > 0)
11775       stored_player[i].move_delay--;
11776
11777     // due to bugs in previous versions, counter must count up, not down
11778     if (stored_player[i].push_delay != -1)
11779       stored_player[i].push_delay++;
11780
11781     if (stored_player[i].drop_delay > 0)
11782       stored_player[i].drop_delay--;
11783
11784     if (stored_player[i].is_dropping_pressed)
11785       stored_player[i].drop_pressed_delay++;
11786   }
11787 }
11788
11789 void AdvanceFrameCounter(void)
11790 {
11791   FrameCounter++;
11792 }
11793
11794 void AdvanceGfxFrame(void)
11795 {
11796   int x, y;
11797
11798   SCAN_PLAYFIELD(x, y)
11799   {
11800     GfxFrame[x][y]++;
11801   }
11802 }
11803
11804 static void HandleMouseAction(struct MouseActionInfo *mouse_action,
11805                               struct MouseActionInfo *mouse_action_last)
11806 {
11807   if (mouse_action->button)
11808   {
11809     int new_button = (mouse_action->button && mouse_action_last->button == 0);
11810     int ch_button = CH_SIDE_FROM_BUTTON(mouse_action->button);
11811     int x = mouse_action->lx;
11812     int y = mouse_action->ly;
11813     int element = Tile[x][y];
11814
11815     if (new_button)
11816     {
11817       CheckElementChangeByMouse(x, y, element, CE_CLICKED_BY_MOUSE, ch_button);
11818       CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_CLICKED_ON_X,
11819                                          ch_button);
11820     }
11821
11822     CheckElementChangeByMouse(x, y, element, CE_PRESSED_BY_MOUSE, ch_button);
11823     CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_PRESSED_ON_X,
11824                                        ch_button);
11825
11826     if (level.use_step_counter)
11827     {
11828       boolean counted_click = FALSE;
11829
11830       // element clicked that can change when clicked/pressed
11831       if (CAN_CHANGE_OR_HAS_ACTION(element) &&
11832           (HAS_ANY_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
11833            HAS_ANY_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE)))
11834         counted_click = TRUE;
11835
11836       // element clicked that can trigger change when clicked/pressed
11837       if (trigger_events[element][CE_MOUSE_CLICKED_ON_X] ||
11838           trigger_events[element][CE_MOUSE_PRESSED_ON_X])
11839         counted_click = TRUE;
11840
11841       if (new_button && counted_click)
11842         CheckLevelTime_StepCounter();
11843     }
11844   }
11845 }
11846
11847 void StartGameActions(boolean init_network_game, boolean record_tape,
11848                       int random_seed)
11849 {
11850   unsigned int new_random_seed = InitRND(random_seed);
11851
11852   if (record_tape)
11853     TapeStartRecording(new_random_seed);
11854
11855   if (setup.auto_pause_on_start && !tape.pausing)
11856     TapeTogglePause(TAPE_TOGGLE_MANUAL);
11857
11858   if (init_network_game)
11859   {
11860     SendToServer_LevelFile();
11861     SendToServer_StartPlaying();
11862
11863     return;
11864   }
11865
11866   InitGame();
11867 }
11868
11869 static void GameActionsExt(void)
11870 {
11871 #if 0
11872   static unsigned int game_frame_delay = 0;
11873 #endif
11874   unsigned int game_frame_delay_value;
11875   byte *recorded_player_action;
11876   byte summarized_player_action = 0;
11877   byte tape_action[MAX_TAPE_ACTIONS] = { 0 };
11878   int i;
11879
11880   // detect endless loops, caused by custom element programming
11881   if (recursion_loop_detected && recursion_loop_depth == 0)
11882   {
11883     char *message = getStringCat3("Internal Error! Element ",
11884                                   EL_NAME(recursion_loop_element),
11885                                   " caused endless loop! Quit the game?");
11886
11887     Warn("element '%s' caused endless loop in game engine",
11888          EL_NAME(recursion_loop_element));
11889
11890     RequestQuitGameExt(program.headless, level_editor_test_game, message);
11891
11892     recursion_loop_detected = FALSE;    // if game should be continued
11893
11894     free(message);
11895
11896     return;
11897   }
11898
11899   if (game.restart_level)
11900     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11901
11902   CheckLevelSolved();
11903
11904   if (game.LevelSolved && !game.LevelSolved_GameEnd)
11905     GameWon();
11906
11907   if (game.all_players_gone && !TAPE_IS_STOPPED(tape))
11908     TapeStop();
11909
11910   if (game_status != GAME_MODE_PLAYING)         // status might have changed
11911     return;
11912
11913   game_frame_delay_value =
11914     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11915
11916   if (tape.playing && tape.warp_forward && !tape.pausing)
11917     game_frame_delay_value = 0;
11918
11919   SetVideoFrameDelay(game_frame_delay_value);
11920
11921   // (de)activate virtual buttons depending on current game status
11922   if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
11923   {
11924     if (game.all_players_gone)  // if no players there to be controlled anymore
11925       SetOverlayActive(FALSE);
11926     else if (!tape.playing)     // if game continues after tape stopped playing
11927       SetOverlayActive(TRUE);
11928   }
11929
11930 #if 0
11931 #if 0
11932   // ---------- main game synchronization point ----------
11933
11934   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11935
11936   Debug("game:playing:skip", "skip == %d", skip);
11937
11938 #else
11939   // ---------- main game synchronization point ----------
11940
11941   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11942 #endif
11943 #endif
11944
11945   if (network_playing && !network_player_action_received)
11946   {
11947     // try to get network player actions in time
11948
11949     // last chance to get network player actions without main loop delay
11950     HandleNetworking();
11951
11952     // game was quit by network peer
11953     if (game_status != GAME_MODE_PLAYING)
11954       return;
11955
11956     // check if network player actions still missing and game still running
11957     if (!network_player_action_received && !checkGameEnded())
11958       return;           // failed to get network player actions in time
11959
11960     // do not yet reset "network_player_action_received" (for tape.pausing)
11961   }
11962
11963   if (tape.pausing)
11964     return;
11965
11966   // at this point we know that we really continue executing the game
11967
11968   network_player_action_received = FALSE;
11969
11970   // when playing tape, read previously recorded player input from tape data
11971   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11972
11973   local_player->effective_mouse_action = local_player->mouse_action;
11974
11975   if (recorded_player_action != NULL)
11976     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11977                                  recorded_player_action);
11978
11979   // TapePlayAction() may return NULL when toggling to "pause before death"
11980   if (tape.pausing)
11981     return;
11982
11983   if (tape.set_centered_player)
11984   {
11985     game.centered_player_nr_next = tape.centered_player_nr_next;
11986     game.set_centered_player = TRUE;
11987   }
11988
11989   for (i = 0; i < MAX_PLAYERS; i++)
11990   {
11991     summarized_player_action |= stored_player[i].action;
11992
11993     if (!network_playing && (game.team_mode || tape.playing))
11994       stored_player[i].effective_action = stored_player[i].action;
11995   }
11996
11997   if (network_playing && !checkGameEnded())
11998     SendToServer_MovePlayer(summarized_player_action);
11999
12000   // summarize all actions at local players mapped input device position
12001   // (this allows using different input devices in single player mode)
12002   if (!network.enabled && !game.team_mode)
12003     stored_player[map_player_action[local_player->index_nr]].effective_action =
12004       summarized_player_action;
12005
12006   // summarize all actions at centered player in local team mode
12007   if (tape.recording &&
12008       setup.team_mode && !network.enabled &&
12009       setup.input_on_focus &&
12010       game.centered_player_nr != -1)
12011   {
12012     for (i = 0; i < MAX_PLAYERS; i++)
12013       stored_player[map_player_action[i]].effective_action =
12014         (i == game.centered_player_nr ? summarized_player_action : 0);
12015   }
12016
12017   if (recorded_player_action != NULL)
12018     for (i = 0; i < MAX_PLAYERS; i++)
12019       stored_player[i].effective_action = recorded_player_action[i];
12020
12021   for (i = 0; i < MAX_PLAYERS; i++)
12022   {
12023     tape_action[i] = stored_player[i].effective_action;
12024
12025     /* (this may happen in the RND game engine if a player was not present on
12026        the playfield on level start, but appeared later from a custom element */
12027     if (setup.team_mode &&
12028         tape.recording &&
12029         tape_action[i] &&
12030         !tape.player_participates[i])
12031       tape.player_participates[i] = TRUE;
12032   }
12033
12034   SetTapeActionFromMouseAction(tape_action,
12035                                &local_player->effective_mouse_action);
12036
12037   // only record actions from input devices, but not programmed actions
12038   if (tape.recording)
12039     TapeRecordAction(tape_action);
12040
12041   // remember if game was played (especially after tape stopped playing)
12042   if (!tape.playing && summarized_player_action && !checkGameFailed())
12043     game.GamePlayed = TRUE;
12044
12045 #if USE_NEW_PLAYER_ASSIGNMENTS
12046   // !!! also map player actions in single player mode !!!
12047   // if (game.team_mode)
12048   if (1)
12049   {
12050     byte mapped_action[MAX_PLAYERS];
12051
12052 #if DEBUG_PLAYER_ACTIONS
12053     for (i = 0; i < MAX_PLAYERS; i++)
12054       DebugContinued("", "%d, ", stored_player[i].effective_action);
12055 #endif
12056
12057     for (i = 0; i < MAX_PLAYERS; i++)
12058       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
12059
12060     for (i = 0; i < MAX_PLAYERS; i++)
12061       stored_player[i].effective_action = mapped_action[i];
12062
12063 #if DEBUG_PLAYER_ACTIONS
12064     DebugContinued("", "=> ");
12065     for (i = 0; i < MAX_PLAYERS; i++)
12066       DebugContinued("", "%d, ", stored_player[i].effective_action);
12067     DebugContinued("game:playing:player", "\n");
12068 #endif
12069   }
12070 #if DEBUG_PLAYER_ACTIONS
12071   else
12072   {
12073     for (i = 0; i < MAX_PLAYERS; i++)
12074       DebugContinued("", "%d, ", stored_player[i].effective_action);
12075     DebugContinued("game:playing:player", "\n");
12076   }
12077 #endif
12078 #endif
12079
12080   for (i = 0; i < MAX_PLAYERS; i++)
12081   {
12082     // allow engine snapshot in case of changed movement attempt
12083     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
12084         (stored_player[i].effective_action & KEY_MOTION))
12085       game.snapshot.changed_action = TRUE;
12086
12087     // allow engine snapshot in case of snapping/dropping attempt
12088     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
12089         (stored_player[i].effective_action & KEY_BUTTON) != 0)
12090       game.snapshot.changed_action = TRUE;
12091
12092     game.snapshot.last_action[i] = stored_player[i].effective_action;
12093   }
12094
12095   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12096   {
12097     GameActions_EM_Main();
12098   }
12099   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12100   {
12101     GameActions_SP_Main();
12102   }
12103   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
12104   {
12105     GameActions_MM_Main();
12106   }
12107   else
12108   {
12109     GameActions_RND_Main();
12110   }
12111
12112   BlitScreenToBitmap(backbuffer);
12113
12114   CheckLevelSolved();
12115   CheckLevelTime();
12116
12117   AdvanceFrameAndPlayerCounters(-1);    // advance counters for all players
12118
12119   if (global.show_frames_per_second)
12120   {
12121     static unsigned int fps_counter = 0;
12122     static int fps_frames = 0;
12123     unsigned int fps_delay_ms = Counter() - fps_counter;
12124
12125     fps_frames++;
12126
12127     if (fps_delay_ms >= 500)    // calculate FPS every 0.5 seconds
12128     {
12129       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
12130
12131       fps_frames = 0;
12132       fps_counter = Counter();
12133
12134       // always draw FPS to screen after FPS value was updated
12135       redraw_mask |= REDRAW_FPS;
12136     }
12137
12138     // only draw FPS if no screen areas are deactivated (invisible warp mode)
12139     if (GetDrawDeactivationMask() == REDRAW_NONE)
12140       redraw_mask |= REDRAW_FPS;
12141   }
12142 }
12143
12144 static void GameActions_CheckSaveEngineSnapshot(void)
12145 {
12146   if (!game.snapshot.save_snapshot)
12147     return;
12148
12149   // clear flag for saving snapshot _before_ saving snapshot
12150   game.snapshot.save_snapshot = FALSE;
12151
12152   SaveEngineSnapshotToList();
12153 }
12154
12155 void GameActions(void)
12156 {
12157   GameActionsExt();
12158
12159   GameActions_CheckSaveEngineSnapshot();
12160 }
12161
12162 void GameActions_EM_Main(void)
12163 {
12164   byte effective_action[MAX_PLAYERS];
12165   int i;
12166
12167   for (i = 0; i < MAX_PLAYERS; i++)
12168     effective_action[i] = stored_player[i].effective_action;
12169
12170   GameActions_EM(effective_action);
12171 }
12172
12173 void GameActions_SP_Main(void)
12174 {
12175   byte effective_action[MAX_PLAYERS];
12176   int i;
12177
12178   for (i = 0; i < MAX_PLAYERS; i++)
12179     effective_action[i] = stored_player[i].effective_action;
12180
12181   GameActions_SP(effective_action);
12182
12183   for (i = 0; i < MAX_PLAYERS; i++)
12184   {
12185     if (stored_player[i].force_dropping)
12186       stored_player[i].action |= KEY_BUTTON_DROP;
12187
12188     stored_player[i].force_dropping = FALSE;
12189   }
12190 }
12191
12192 void GameActions_MM_Main(void)
12193 {
12194   AdvanceGfxFrame();
12195
12196   GameActions_MM(local_player->effective_mouse_action);
12197 }
12198
12199 void GameActions_RND_Main(void)
12200 {
12201   GameActions_RND();
12202 }
12203
12204 void GameActions_RND(void)
12205 {
12206   static struct MouseActionInfo mouse_action_last = { 0 };
12207   struct MouseActionInfo mouse_action = local_player->effective_mouse_action;
12208   int magic_wall_x = 0, magic_wall_y = 0;
12209   int i, x, y, element, graphic, last_gfx_frame;
12210
12211   InitPlayfieldScanModeVars();
12212
12213   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12214   {
12215     SCAN_PLAYFIELD(x, y)
12216     {
12217       ChangeCount[x][y] = 0;
12218       ChangeEvent[x][y] = -1;
12219     }
12220   }
12221
12222   if (game.set_centered_player)
12223   {
12224     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12225
12226     // switching to "all players" only possible if all players fit to screen
12227     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12228     {
12229       game.centered_player_nr_next = game.centered_player_nr;
12230       game.set_centered_player = FALSE;
12231     }
12232
12233     // do not switch focus to non-existing (or non-active) player
12234     if (game.centered_player_nr_next >= 0 &&
12235         !stored_player[game.centered_player_nr_next].active)
12236     {
12237       game.centered_player_nr_next = game.centered_player_nr;
12238       game.set_centered_player = FALSE;
12239     }
12240   }
12241
12242   if (game.set_centered_player &&
12243       ScreenMovPos == 0)        // screen currently aligned at tile position
12244   {
12245     int sx, sy;
12246
12247     if (game.centered_player_nr_next == -1)
12248     {
12249       setScreenCenteredToAllPlayers(&sx, &sy);
12250     }
12251     else
12252     {
12253       sx = stored_player[game.centered_player_nr_next].jx;
12254       sy = stored_player[game.centered_player_nr_next].jy;
12255     }
12256
12257     game.centered_player_nr = game.centered_player_nr_next;
12258     game.set_centered_player = FALSE;
12259
12260     DrawRelocateScreen(0, 0, sx, sy, TRUE, setup.quick_switch);
12261     DrawGameDoorValues();
12262   }
12263
12264   // check single step mode (set flag and clear again if any player is active)
12265   game.enter_single_step_mode =
12266     (tape.single_step && tape.recording && !tape.pausing);
12267
12268   for (i = 0; i < MAX_PLAYERS; i++)
12269   {
12270     int actual_player_action = stored_player[i].effective_action;
12271
12272 #if 1
12273     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12274        - rnd_equinox_tetrachloride 048
12275        - rnd_equinox_tetrachloride_ii 096
12276        - rnd_emanuel_schmieg 002
12277        - doctor_sloan_ww 001, 020
12278     */
12279     if (stored_player[i].MovPos == 0)
12280       CheckGravityMovement(&stored_player[i]);
12281 #endif
12282
12283     // overwrite programmed action with tape action
12284     if (stored_player[i].programmed_action)
12285       actual_player_action = stored_player[i].programmed_action;
12286
12287     PlayerActions(&stored_player[i], actual_player_action);
12288
12289     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12290   }
12291
12292   // single step pause mode may already have been toggled by "ScrollPlayer()"
12293   if (game.enter_single_step_mode && !tape.pausing)
12294     TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12295
12296   ScrollScreen(NULL, SCROLL_GO_ON);
12297
12298   /* for backwards compatibility, the following code emulates a fixed bug that
12299      occured when pushing elements (causing elements that just made their last
12300      pushing step to already (if possible) make their first falling step in the
12301      same game frame, which is bad); this code is also needed to use the famous
12302      "spring push bug" which is used in older levels and might be wanted to be
12303      used also in newer levels, but in this case the buggy pushing code is only
12304      affecting the "spring" element and no other elements */
12305
12306   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12307   {
12308     for (i = 0; i < MAX_PLAYERS; i++)
12309     {
12310       struct PlayerInfo *player = &stored_player[i];
12311       int x = player->jx;
12312       int y = player->jy;
12313
12314       if (player->active && player->is_pushing && player->is_moving &&
12315           IS_MOVING(x, y) &&
12316           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12317            Tile[x][y] == EL_SPRING))
12318       {
12319         ContinueMoving(x, y);
12320
12321         // continue moving after pushing (this is actually a bug)
12322         if (!IS_MOVING(x, y))
12323           Stop[x][y] = FALSE;
12324       }
12325     }
12326   }
12327
12328   SCAN_PLAYFIELD(x, y)
12329   {
12330     Last[x][y] = Tile[x][y];
12331
12332     ChangeCount[x][y] = 0;
12333     ChangeEvent[x][y] = -1;
12334
12335     // this must be handled before main playfield loop
12336     if (Tile[x][y] == EL_PLAYER_IS_LEAVING)
12337     {
12338       MovDelay[x][y]--;
12339       if (MovDelay[x][y] <= 0)
12340         RemoveField(x, y);
12341     }
12342
12343     if (Tile[x][y] == EL_ELEMENT_SNAPPING)
12344     {
12345       MovDelay[x][y]--;
12346       if (MovDelay[x][y] <= 0)
12347       {
12348         int element = Store[x][y];
12349         int move_direction = MovDir[x][y];
12350         int player_index_bit = Store2[x][y];
12351
12352         Store[x][y] = 0;
12353         Store2[x][y] = 0;
12354
12355         RemoveField(x, y);
12356         TEST_DrawLevelField(x, y);
12357
12358         TestFieldAfterSnapping(x, y, element, move_direction, player_index_bit);
12359
12360         if (IS_ENVELOPE(element))
12361           local_player->show_envelope = element;
12362       }
12363     }
12364
12365 #if DEBUG
12366     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12367     {
12368       Debug("game:playing:GameActions_RND", "x = %d, y = %d: ChangePage != -1",
12369             x, y);
12370       Debug("game:playing:GameActions_RND", "This should never happen!");
12371
12372       ChangePage[x][y] = -1;
12373     }
12374 #endif
12375
12376     Stop[x][y] = FALSE;
12377     if (WasJustMoving[x][y] > 0)
12378       WasJustMoving[x][y]--;
12379     if (WasJustFalling[x][y] > 0)
12380       WasJustFalling[x][y]--;
12381     if (CheckCollision[x][y] > 0)
12382       CheckCollision[x][y]--;
12383     if (CheckImpact[x][y] > 0)
12384       CheckImpact[x][y]--;
12385
12386     GfxFrame[x][y]++;
12387
12388     /* reset finished pushing action (not done in ContinueMoving() to allow
12389        continuous pushing animation for elements with zero push delay) */
12390     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12391     {
12392       ResetGfxAnimation(x, y);
12393       TEST_DrawLevelField(x, y);
12394     }
12395
12396 #if DEBUG
12397     if (IS_BLOCKED(x, y))
12398     {
12399       int oldx, oldy;
12400
12401       Blocked2Moving(x, y, &oldx, &oldy);
12402       if (!IS_MOVING(oldx, oldy))
12403       {
12404         Debug("game:playing:GameActions_RND", "(BLOCKED => MOVING) context corrupted!");
12405         Debug("game:playing:GameActions_RND", "BLOCKED: x = %d, y = %d", x, y);
12406         Debug("game:playing:GameActions_RND", "!MOVING: oldx = %d, oldy = %d", oldx, oldy);
12407         Debug("game:playing:GameActions_RND", "This should never happen!");
12408       }
12409     }
12410 #endif
12411   }
12412
12413   HandleMouseAction(&mouse_action, &mouse_action_last);
12414
12415   SCAN_PLAYFIELD(x, y)
12416   {
12417     element = Tile[x][y];
12418     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12419     last_gfx_frame = GfxFrame[x][y];
12420
12421     if (element == EL_EMPTY)
12422       graphic = el2img(GfxElementEmpty[x][y]);
12423
12424     ResetGfxFrame(x, y);
12425
12426     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
12427       DrawLevelGraphicAnimation(x, y, graphic);
12428
12429     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12430         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12431       ResetRandomAnimationValue(x, y);
12432
12433     SetRandomAnimationValue(x, y);
12434
12435     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12436
12437     if (IS_INACTIVE(element))
12438     {
12439       if (IS_ANIMATED(graphic))
12440         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12441
12442       continue;
12443     }
12444
12445     // this may take place after moving, so 'element' may have changed
12446     if (IS_CHANGING(x, y) &&
12447         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12448     {
12449       int page = element_info[element].event_page_nr[CE_DELAY];
12450
12451       HandleElementChange(x, y, page);
12452
12453       element = Tile[x][y];
12454       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12455     }
12456
12457     CheckNextToConditions(x, y);
12458
12459     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12460     {
12461       StartMoving(x, y);
12462
12463       element = Tile[x][y];
12464       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12465
12466       if (IS_ANIMATED(graphic) &&
12467           !IS_MOVING(x, y) &&
12468           !Stop[x][y])
12469         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12470
12471       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12472         TEST_DrawTwinkleOnField(x, y);
12473     }
12474     else if (element == EL_ACID)
12475     {
12476       if (!Stop[x][y])
12477         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12478     }
12479     else if ((element == EL_EXIT_OPEN ||
12480               element == EL_EM_EXIT_OPEN ||
12481               element == EL_SP_EXIT_OPEN ||
12482               element == EL_STEEL_EXIT_OPEN ||
12483               element == EL_EM_STEEL_EXIT_OPEN ||
12484               element == EL_SP_TERMINAL ||
12485               element == EL_SP_TERMINAL_ACTIVE ||
12486               element == EL_EXTRA_TIME ||
12487               element == EL_SHIELD_NORMAL ||
12488               element == EL_SHIELD_DEADLY) &&
12489              IS_ANIMATED(graphic))
12490       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12491     else if (IS_MOVING(x, y))
12492       ContinueMoving(x, y);
12493     else if (IS_ACTIVE_BOMB(element))
12494       CheckDynamite(x, y);
12495     else if (element == EL_AMOEBA_GROWING)
12496       AmoebaGrowing(x, y);
12497     else if (element == EL_AMOEBA_SHRINKING)
12498       AmoebaShrinking(x, y);
12499
12500 #if !USE_NEW_AMOEBA_CODE
12501     else if (IS_AMOEBALIVE(element))
12502       AmoebaReproduce(x, y);
12503 #endif
12504
12505     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12506       Life(x, y);
12507     else if (element == EL_EXIT_CLOSED)
12508       CheckExit(x, y);
12509     else if (element == EL_EM_EXIT_CLOSED)
12510       CheckExitEM(x, y);
12511     else if (element == EL_STEEL_EXIT_CLOSED)
12512       CheckExitSteel(x, y);
12513     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12514       CheckExitSteelEM(x, y);
12515     else if (element == EL_SP_EXIT_CLOSED)
12516       CheckExitSP(x, y);
12517     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12518              element == EL_EXPANDABLE_STEELWALL_GROWING)
12519       WallGrowing(x, y);
12520     else if (element == EL_EXPANDABLE_WALL ||
12521              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12522              element == EL_EXPANDABLE_WALL_VERTICAL ||
12523              element == EL_EXPANDABLE_WALL_ANY ||
12524              element == EL_BD_EXPANDABLE_WALL ||
12525              element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12526              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12527              element == EL_EXPANDABLE_STEELWALL_ANY)
12528       CheckWallGrowing(x, y);
12529     else if (element == EL_FLAMES)
12530       CheckForDragon(x, y);
12531     else if (element == EL_EXPLOSION)
12532       ; // drawing of correct explosion animation is handled separately
12533     else if (element == EL_ELEMENT_SNAPPING ||
12534              element == EL_DIAGONAL_SHRINKING ||
12535              element == EL_DIAGONAL_GROWING)
12536     {
12537       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y], GfxDir[x][y]);
12538
12539       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12540     }
12541     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12542       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12543
12544     if (IS_BELT_ACTIVE(element))
12545       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12546
12547     if (game.magic_wall_active)
12548     {
12549       int jx = local_player->jx, jy = local_player->jy;
12550
12551       // play the element sound at the position nearest to the player
12552       if ((element == EL_MAGIC_WALL_FULL ||
12553            element == EL_MAGIC_WALL_ACTIVE ||
12554            element == EL_MAGIC_WALL_EMPTYING ||
12555            element == EL_BD_MAGIC_WALL_FULL ||
12556            element == EL_BD_MAGIC_WALL_ACTIVE ||
12557            element == EL_BD_MAGIC_WALL_EMPTYING ||
12558            element == EL_DC_MAGIC_WALL_FULL ||
12559            element == EL_DC_MAGIC_WALL_ACTIVE ||
12560            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12561           ABS(x - jx) + ABS(y - jy) <
12562           ABS(magic_wall_x - jx) + ABS(magic_wall_y - jy))
12563       {
12564         magic_wall_x = x;
12565         magic_wall_y = y;
12566       }
12567     }
12568   }
12569
12570 #if USE_NEW_AMOEBA_CODE
12571   // new experimental amoeba growth stuff
12572   if (!(FrameCounter % 8))
12573   {
12574     static unsigned int random = 1684108901;
12575
12576     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12577     {
12578       x = RND(lev_fieldx);
12579       y = RND(lev_fieldy);
12580       element = Tile[x][y];
12581
12582       if (!IS_PLAYER(x, y) &&
12583           (element == EL_EMPTY ||
12584            CAN_GROW_INTO(element) ||
12585            element == EL_QUICKSAND_EMPTY ||
12586            element == EL_QUICKSAND_FAST_EMPTY ||
12587            element == EL_ACID_SPLASH_LEFT ||
12588            element == EL_ACID_SPLASH_RIGHT))
12589       {
12590         if ((IN_LEV_FIELD(x, y - 1) && Tile[x][y - 1] == EL_AMOEBA_WET) ||
12591             (IN_LEV_FIELD(x - 1, y) && Tile[x - 1][y] == EL_AMOEBA_WET) ||
12592             (IN_LEV_FIELD(x + 1, y) && Tile[x + 1][y] == EL_AMOEBA_WET) ||
12593             (IN_LEV_FIELD(x, y + 1) && Tile[x][y + 1] == EL_AMOEBA_WET))
12594           Tile[x][y] = EL_AMOEBA_DROP;
12595       }
12596
12597       random = random * 129 + 1;
12598     }
12599   }
12600 #endif
12601
12602   game.explosions_delayed = FALSE;
12603
12604   SCAN_PLAYFIELD(x, y)
12605   {
12606     element = Tile[x][y];
12607
12608     if (ExplodeField[x][y])
12609       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12610     else if (element == EL_EXPLOSION)
12611       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12612
12613     ExplodeField[x][y] = EX_TYPE_NONE;
12614   }
12615
12616   game.explosions_delayed = TRUE;
12617
12618   if (game.magic_wall_active)
12619   {
12620     if (!(game.magic_wall_time_left % 4))
12621     {
12622       int element = Tile[magic_wall_x][magic_wall_y];
12623
12624       if (element == EL_BD_MAGIC_WALL_FULL ||
12625           element == EL_BD_MAGIC_WALL_ACTIVE ||
12626           element == EL_BD_MAGIC_WALL_EMPTYING)
12627         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12628       else if (element == EL_DC_MAGIC_WALL_FULL ||
12629                element == EL_DC_MAGIC_WALL_ACTIVE ||
12630                element == EL_DC_MAGIC_WALL_EMPTYING)
12631         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12632       else
12633         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12634     }
12635
12636     if (game.magic_wall_time_left > 0)
12637     {
12638       game.magic_wall_time_left--;
12639
12640       if (!game.magic_wall_time_left)
12641       {
12642         SCAN_PLAYFIELD(x, y)
12643         {
12644           element = Tile[x][y];
12645
12646           if (element == EL_MAGIC_WALL_ACTIVE ||
12647               element == EL_MAGIC_WALL_FULL)
12648           {
12649             Tile[x][y] = EL_MAGIC_WALL_DEAD;
12650             TEST_DrawLevelField(x, y);
12651           }
12652           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12653                    element == EL_BD_MAGIC_WALL_FULL)
12654           {
12655             Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
12656             TEST_DrawLevelField(x, y);
12657           }
12658           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12659                    element == EL_DC_MAGIC_WALL_FULL)
12660           {
12661             Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
12662             TEST_DrawLevelField(x, y);
12663           }
12664         }
12665
12666         game.magic_wall_active = FALSE;
12667       }
12668     }
12669   }
12670
12671   if (game.light_time_left > 0)
12672   {
12673     game.light_time_left--;
12674
12675     if (game.light_time_left == 0)
12676       RedrawAllLightSwitchesAndInvisibleElements();
12677   }
12678
12679   if (game.timegate_time_left > 0)
12680   {
12681     game.timegate_time_left--;
12682
12683     if (game.timegate_time_left == 0)
12684       CloseAllOpenTimegates();
12685   }
12686
12687   if (game.lenses_time_left > 0)
12688   {
12689     game.lenses_time_left--;
12690
12691     if (game.lenses_time_left == 0)
12692       RedrawAllInvisibleElementsForLenses();
12693   }
12694
12695   if (game.magnify_time_left > 0)
12696   {
12697     game.magnify_time_left--;
12698
12699     if (game.magnify_time_left == 0)
12700       RedrawAllInvisibleElementsForMagnifier();
12701   }
12702
12703   for (i = 0; i < MAX_PLAYERS; i++)
12704   {
12705     struct PlayerInfo *player = &stored_player[i];
12706
12707     if (SHIELD_ON(player))
12708     {
12709       if (player->shield_deadly_time_left)
12710         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12711       else if (player->shield_normal_time_left)
12712         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12713     }
12714   }
12715
12716 #if USE_DELAYED_GFX_REDRAW
12717   SCAN_PLAYFIELD(x, y)
12718   {
12719     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12720     {
12721       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12722          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12723
12724       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12725         DrawLevelField(x, y);
12726
12727       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12728         DrawLevelFieldCrumbled(x, y);
12729
12730       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12731         DrawLevelFieldCrumbledNeighbours(x, y);
12732
12733       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12734         DrawTwinkleOnField(x, y);
12735     }
12736
12737     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12738   }
12739 #endif
12740
12741   DrawAllPlayers();
12742   PlayAllPlayersSound();
12743
12744   for (i = 0; i < MAX_PLAYERS; i++)
12745   {
12746     struct PlayerInfo *player = &stored_player[i];
12747
12748     if (player->show_envelope != 0 && (!player->active ||
12749                                        player->MovPos == 0))
12750     {
12751       ShowEnvelope(player->show_envelope - EL_ENVELOPE_1);
12752
12753       player->show_envelope = 0;
12754     }
12755   }
12756
12757   // use random number generator in every frame to make it less predictable
12758   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12759     RND(1);
12760
12761   mouse_action_last = mouse_action;
12762 }
12763
12764 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12765 {
12766   int min_x = x, min_y = y, max_x = x, max_y = y;
12767   int scr_fieldx = getScreenFieldSizeX();
12768   int scr_fieldy = getScreenFieldSizeY();
12769   int i;
12770
12771   for (i = 0; i < MAX_PLAYERS; i++)
12772   {
12773     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12774
12775     if (!stored_player[i].active || &stored_player[i] == player)
12776       continue;
12777
12778     min_x = MIN(min_x, jx);
12779     min_y = MIN(min_y, jy);
12780     max_x = MAX(max_x, jx);
12781     max_y = MAX(max_y, jy);
12782   }
12783
12784   return (max_x - min_x < scr_fieldx && max_y - min_y < scr_fieldy);
12785 }
12786
12787 static boolean AllPlayersInVisibleScreen(void)
12788 {
12789   int i;
12790
12791   for (i = 0; i < MAX_PLAYERS; i++)
12792   {
12793     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12794
12795     if (!stored_player[i].active)
12796       continue;
12797
12798     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12799       return FALSE;
12800   }
12801
12802   return TRUE;
12803 }
12804
12805 void ScrollLevel(int dx, int dy)
12806 {
12807   int scroll_offset = 2 * TILEX_VAR;
12808   int x, y;
12809
12810   BlitBitmap(drawto_field, drawto_field,
12811              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12812              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12813              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12814              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12815              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12816              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12817
12818   if (dx != 0)
12819   {
12820     x = (dx == 1 ? BX1 : BX2);
12821     for (y = BY1; y <= BY2; y++)
12822       DrawScreenField(x, y);
12823   }
12824
12825   if (dy != 0)
12826   {
12827     y = (dy == 1 ? BY1 : BY2);
12828     for (x = BX1; x <= BX2; x++)
12829       DrawScreenField(x, y);
12830   }
12831
12832   redraw_mask |= REDRAW_FIELD;
12833 }
12834
12835 static boolean canFallDown(struct PlayerInfo *player)
12836 {
12837   int jx = player->jx, jy = player->jy;
12838
12839   return (IN_LEV_FIELD(jx, jy + 1) &&
12840           (IS_FREE(jx, jy + 1) ||
12841            (Tile[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12842           IS_WALKABLE_FROM(Tile[jx][jy], MV_DOWN) &&
12843           !IS_WALKABLE_INSIDE(Tile[jx][jy]));
12844 }
12845
12846 static boolean canPassField(int x, int y, int move_dir)
12847 {
12848   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12849   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12850   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12851   int nextx = x + dx;
12852   int nexty = y + dy;
12853   int element = Tile[x][y];
12854
12855   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12856           !CAN_MOVE(element) &&
12857           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12858           IS_WALKABLE_FROM(Tile[nextx][nexty], move_dir) &&
12859           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12860 }
12861
12862 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12863 {
12864   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12865   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12866   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12867   int newx = x + dx;
12868   int newy = y + dy;
12869
12870   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12871           IS_GRAVITY_REACHABLE(Tile[newx][newy]) &&
12872           (IS_DIGGABLE(Tile[newx][newy]) ||
12873            IS_WALKABLE_FROM(Tile[newx][newy], opposite_dir) ||
12874            canPassField(newx, newy, move_dir)));
12875 }
12876
12877 static void CheckGravityMovement(struct PlayerInfo *player)
12878 {
12879   if (player->gravity && !player->programmed_action)
12880   {
12881     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12882     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12883     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12884     int jx = player->jx, jy = player->jy;
12885     boolean player_is_moving_to_valid_field =
12886       (!player_is_snapping &&
12887        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12888         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12889     boolean player_can_fall_down = canFallDown(player);
12890
12891     if (player_can_fall_down &&
12892         !player_is_moving_to_valid_field)
12893       player->programmed_action = MV_DOWN;
12894   }
12895 }
12896
12897 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12898 {
12899   return CheckGravityMovement(player);
12900
12901   if (player->gravity && !player->programmed_action)
12902   {
12903     int jx = player->jx, jy = player->jy;
12904     boolean field_under_player_is_free =
12905       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12906     boolean player_is_standing_on_valid_field =
12907       (IS_WALKABLE_INSIDE(Tile[jx][jy]) ||
12908        (IS_WALKABLE(Tile[jx][jy]) &&
12909         !(element_info[Tile[jx][jy]].access_direction & MV_DOWN)));
12910
12911     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12912       player->programmed_action = MV_DOWN;
12913   }
12914 }
12915
12916 /*
12917   MovePlayerOneStep()
12918   -----------------------------------------------------------------------------
12919   dx, dy:               direction (non-diagonal) to try to move the player to
12920   real_dx, real_dy:     direction as read from input device (can be diagonal)
12921 */
12922
12923 boolean MovePlayerOneStep(struct PlayerInfo *player,
12924                           int dx, int dy, int real_dx, int real_dy)
12925 {
12926   int jx = player->jx, jy = player->jy;
12927   int new_jx = jx + dx, new_jy = jy + dy;
12928   int can_move;
12929   boolean player_can_move = !player->cannot_move;
12930
12931   if (!player->active || (!dx && !dy))
12932     return MP_NO_ACTION;
12933
12934   player->MovDir = (dx < 0 ? MV_LEFT :
12935                     dx > 0 ? MV_RIGHT :
12936                     dy < 0 ? MV_UP :
12937                     dy > 0 ? MV_DOWN :  MV_NONE);
12938
12939   if (!IN_LEV_FIELD(new_jx, new_jy))
12940     return MP_NO_ACTION;
12941
12942   if (!player_can_move)
12943   {
12944     if (player->MovPos == 0)
12945     {
12946       player->is_moving = FALSE;
12947       player->is_digging = FALSE;
12948       player->is_collecting = FALSE;
12949       player->is_snapping = FALSE;
12950       player->is_pushing = FALSE;
12951     }
12952   }
12953
12954   if (!network.enabled && game.centered_player_nr == -1 &&
12955       !AllPlayersInSight(player, new_jx, new_jy))
12956     return MP_NO_ACTION;
12957
12958   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx, real_dy, DF_DIG);
12959   if (can_move != MP_MOVING)
12960     return can_move;
12961
12962   // check if DigField() has caused relocation of the player
12963   if (player->jx != jx || player->jy != jy)
12964     return MP_NO_ACTION;        // <-- !!! CHECK THIS [-> MP_ACTION ?] !!!
12965
12966   StorePlayer[jx][jy] = 0;
12967   player->last_jx = jx;
12968   player->last_jy = jy;
12969   player->jx = new_jx;
12970   player->jy = new_jy;
12971   StorePlayer[new_jx][new_jy] = player->element_nr;
12972
12973   if (player->move_delay_value_next != -1)
12974   {
12975     player->move_delay_value = player->move_delay_value_next;
12976     player->move_delay_value_next = -1;
12977   }
12978
12979   player->MovPos =
12980     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12981
12982   player->step_counter++;
12983
12984   PlayerVisit[jx][jy] = FrameCounter;
12985
12986   player->is_moving = TRUE;
12987
12988 #if 1
12989   // should better be called in MovePlayer(), but this breaks some tapes
12990   ScrollPlayer(player, SCROLL_INIT);
12991 #endif
12992
12993   return MP_MOVING;
12994 }
12995
12996 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12997 {
12998   int jx = player->jx, jy = player->jy;
12999   int old_jx = jx, old_jy = jy;
13000   int moved = MP_NO_ACTION;
13001
13002   if (!player->active)
13003     return FALSE;
13004
13005   if (!dx && !dy)
13006   {
13007     if (player->MovPos == 0)
13008     {
13009       player->is_moving = FALSE;
13010       player->is_digging = FALSE;
13011       player->is_collecting = FALSE;
13012       player->is_snapping = FALSE;
13013       player->is_pushing = FALSE;
13014     }
13015
13016     return FALSE;
13017   }
13018
13019   if (player->move_delay > 0)
13020     return FALSE;
13021
13022   player->move_delay = -1;              // set to "uninitialized" value
13023
13024   // store if player is automatically moved to next field
13025   player->is_auto_moving = (player->programmed_action != MV_NONE);
13026
13027   // remove the last programmed player action
13028   player->programmed_action = 0;
13029
13030   if (player->MovPos)
13031   {
13032     // should only happen if pre-1.2 tape recordings are played
13033     // this is only for backward compatibility
13034
13035     int original_move_delay_value = player->move_delay_value;
13036
13037 #if DEBUG
13038     Debug("game:playing:MovePlayer",
13039           "THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]",
13040           tape.counter);
13041 #endif
13042
13043     // scroll remaining steps with finest movement resolution
13044     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
13045
13046     while (player->MovPos)
13047     {
13048       ScrollPlayer(player, SCROLL_GO_ON);
13049       ScrollScreen(NULL, SCROLL_GO_ON);
13050
13051       AdvanceFrameAndPlayerCounters(player->index_nr);
13052
13053       DrawAllPlayers();
13054       BackToFront_WithFrameDelay(0);
13055     }
13056
13057     player->move_delay_value = original_move_delay_value;
13058   }
13059
13060   player->is_active = FALSE;
13061
13062   if (player->last_move_dir & MV_HORIZONTAL)
13063   {
13064     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
13065       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
13066   }
13067   else
13068   {
13069     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
13070       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
13071   }
13072
13073   if (!moved && !player->is_active)
13074   {
13075     player->is_moving = FALSE;
13076     player->is_digging = FALSE;
13077     player->is_collecting = FALSE;
13078     player->is_snapping = FALSE;
13079     player->is_pushing = FALSE;
13080   }
13081
13082   jx = player->jx;
13083   jy = player->jy;
13084
13085   if (moved & MP_MOVING && !ScreenMovPos &&
13086       (player->index_nr == game.centered_player_nr ||
13087        game.centered_player_nr == -1))
13088   {
13089     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
13090
13091     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13092     {
13093       // actual player has left the screen -- scroll in that direction
13094       if (jx != old_jx)         // player has moved horizontally
13095         scroll_x += (jx - old_jx);
13096       else                      // player has moved vertically
13097         scroll_y += (jy - old_jy);
13098     }
13099     else
13100     {
13101       int offset_raw = game.scroll_delay_value;
13102
13103       if (jx != old_jx)         // player has moved horizontally
13104       {
13105         int offset = MIN(offset_raw, (SCR_FIELDX - 2) / 2);
13106         int offset_x = offset * (player->MovDir == MV_LEFT ? +1 : -1);
13107         int new_scroll_x = jx - MIDPOSX + offset_x;
13108
13109         if ((player->MovDir == MV_LEFT  && scroll_x > new_scroll_x) ||
13110             (player->MovDir == MV_RIGHT && scroll_x < new_scroll_x))
13111           scroll_x = new_scroll_x;
13112
13113         // don't scroll over playfield boundaries
13114         scroll_x = MIN(MAX(SBX_Left, scroll_x), SBX_Right);
13115
13116         // don't scroll more than one field at a time
13117         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13118
13119         // don't scroll against the player's moving direction
13120         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
13121             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13122           scroll_x = old_scroll_x;
13123       }
13124       else                      // player has moved vertically
13125       {
13126         int offset = MIN(offset_raw, (SCR_FIELDY - 2) / 2);
13127         int offset_y = offset * (player->MovDir == MV_UP ? +1 : -1);
13128         int new_scroll_y = jy - MIDPOSY + offset_y;
13129
13130         if ((player->MovDir == MV_UP   && scroll_y > new_scroll_y) ||
13131             (player->MovDir == MV_DOWN && scroll_y < new_scroll_y))
13132           scroll_y = new_scroll_y;
13133
13134         // don't scroll over playfield boundaries
13135         scroll_y = MIN(MAX(SBY_Upper, scroll_y), SBY_Lower);
13136
13137         // don't scroll more than one field at a time
13138         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13139
13140         // don't scroll against the player's moving direction
13141         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
13142             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13143           scroll_y = old_scroll_y;
13144       }
13145     }
13146
13147     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13148     {
13149       if (!network.enabled && game.centered_player_nr == -1 &&
13150           !AllPlayersInVisibleScreen())
13151       {
13152         scroll_x = old_scroll_x;
13153         scroll_y = old_scroll_y;
13154       }
13155       else
13156       {
13157         ScrollScreen(player, SCROLL_INIT);
13158         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13159       }
13160     }
13161   }
13162
13163   player->StepFrame = 0;
13164
13165   if (moved & MP_MOVING)
13166   {
13167     if (old_jx != jx && old_jy == jy)
13168       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13169     else if (old_jx == jx && old_jy != jy)
13170       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13171
13172     TEST_DrawLevelField(jx, jy);        // for "crumbled sand"
13173
13174     player->last_move_dir = player->MovDir;
13175     player->is_moving = TRUE;
13176     player->is_snapping = FALSE;
13177     player->is_switching = FALSE;
13178     player->is_dropping = FALSE;
13179     player->is_dropping_pressed = FALSE;
13180     player->drop_pressed_delay = 0;
13181
13182 #if 0
13183     // should better be called here than above, but this breaks some tapes
13184     ScrollPlayer(player, SCROLL_INIT);
13185 #endif
13186   }
13187   else
13188   {
13189     CheckGravityMovementWhenNotMoving(player);
13190
13191     player->is_moving = FALSE;
13192
13193     /* at this point, the player is allowed to move, but cannot move right now
13194        (e.g. because of something blocking the way) -- ensure that the player
13195        is also allowed to move in the next frame (in old versions before 3.1.1,
13196        the player was forced to wait again for eight frames before next try) */
13197
13198     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13199       player->move_delay = 0;   // allow direct movement in the next frame
13200   }
13201
13202   if (player->move_delay == -1)         // not yet initialized by DigField()
13203     player->move_delay = player->move_delay_value;
13204
13205   if (game.engine_version < VERSION_IDENT(3,0,7,0))
13206   {
13207     TestIfPlayerTouchesBadThing(jx, jy);
13208     TestIfPlayerTouchesCustomElement(jx, jy);
13209   }
13210
13211   if (!player->active)
13212     RemovePlayer(player);
13213
13214   return moved;
13215 }
13216
13217 void ScrollPlayer(struct PlayerInfo *player, int mode)
13218 {
13219   int jx = player->jx, jy = player->jy;
13220   int last_jx = player->last_jx, last_jy = player->last_jy;
13221   int move_stepsize = TILEX / player->move_delay_value;
13222
13223   if (!player->active)
13224     return;
13225
13226   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      // player not moving
13227     return;
13228
13229   if (mode == SCROLL_INIT)
13230   {
13231     player->actual_frame_counter.count = FrameCounter;
13232     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13233
13234     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13235         Tile[last_jx][last_jy] == EL_EMPTY)
13236     {
13237       int last_field_block_delay = 0;   // start with no blocking at all
13238       int block_delay_adjustment = player->block_delay_adjustment;
13239
13240       // if player blocks last field, add delay for exactly one move
13241       if (player->block_last_field)
13242       {
13243         last_field_block_delay += player->move_delay_value;
13244
13245         // when blocking enabled, prevent moving up despite gravity
13246         if (player->gravity && player->MovDir == MV_UP)
13247           block_delay_adjustment = -1;
13248       }
13249
13250       // add block delay adjustment (also possible when not blocking)
13251       last_field_block_delay += block_delay_adjustment;
13252
13253       Tile[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13254       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13255     }
13256
13257     if (player->MovPos != 0)    // player has not yet reached destination
13258       return;
13259   }
13260   else if (!FrameReached(&player->actual_frame_counter))
13261     return;
13262
13263   if (player->MovPos != 0)
13264   {
13265     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13266     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13267
13268     // before DrawPlayer() to draw correct player graphic for this case
13269     if (player->MovPos == 0)
13270       CheckGravityMovement(player);
13271   }
13272
13273   if (player->MovPos == 0)      // player reached destination field
13274   {
13275     if (player->move_delay_reset_counter > 0)
13276     {
13277       player->move_delay_reset_counter--;
13278
13279       if (player->move_delay_reset_counter == 0)
13280       {
13281         // continue with normal speed after quickly moving through gate
13282         HALVE_PLAYER_SPEED(player);
13283
13284         // be able to make the next move without delay
13285         player->move_delay = 0;
13286       }
13287     }
13288
13289     if (Tile[jx][jy] == EL_EXIT_OPEN ||
13290         Tile[jx][jy] == EL_EM_EXIT_OPEN ||
13291         Tile[jx][jy] == EL_EM_EXIT_OPENING ||
13292         Tile[jx][jy] == EL_STEEL_EXIT_OPEN ||
13293         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13294         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
13295         Tile[jx][jy] == EL_SP_EXIT_OPEN ||
13296         Tile[jx][jy] == EL_SP_EXIT_OPENING)     // <-- special case
13297     {
13298       ExitPlayer(player);
13299
13300       if (game.players_still_needed == 0 &&
13301           (game.friends_still_needed == 0 ||
13302            IS_SP_ELEMENT(Tile[jx][jy])))
13303         LevelSolved();
13304     }
13305
13306     player->last_jx = jx;
13307     player->last_jy = jy;
13308
13309     // this breaks one level: "machine", level 000
13310     {
13311       int move_direction = player->MovDir;
13312       int enter_side = MV_DIR_OPPOSITE(move_direction);
13313       int leave_side = move_direction;
13314       int old_jx = last_jx;
13315       int old_jy = last_jy;
13316       int old_element = Tile[old_jx][old_jy];
13317       int new_element = Tile[jx][jy];
13318
13319       if (IS_CUSTOM_ELEMENT(old_element))
13320         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13321                                    CE_LEFT_BY_PLAYER,
13322                                    player->index_bit, leave_side);
13323
13324       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13325                                           CE_PLAYER_LEAVES_X,
13326                                           player->index_bit, leave_side);
13327
13328       // needed because pushed element has not yet reached its destination,
13329       // so it would trigger a change event at its previous field location
13330       if (!player->is_pushing)
13331       {
13332         if (IS_CUSTOM_ELEMENT(new_element))
13333           CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13334                                      player->index_bit, enter_side);
13335
13336         CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13337                                             CE_PLAYER_ENTERS_X,
13338                                             player->index_bit, enter_side);
13339       }
13340
13341       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13342                                         CE_MOVE_OF_X, move_direction);
13343     }
13344
13345     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13346     {
13347       TestIfPlayerTouchesBadThing(jx, jy);
13348       TestIfPlayerTouchesCustomElement(jx, jy);
13349
13350       // needed because pushed element has not yet reached its destination,
13351       // so it would trigger a change event at its previous field location
13352       if (!player->is_pushing)
13353         TestIfElementTouchesCustomElement(jx, jy);      // for empty space
13354
13355       if (level.finish_dig_collect &&
13356           (player->is_digging || player->is_collecting))
13357       {
13358         int last_element = player->last_removed_element;
13359         int move_direction = player->MovDir;
13360         int enter_side = MV_DIR_OPPOSITE(move_direction);
13361         int change_event = (player->is_digging ? CE_PLAYER_DIGS_X :
13362                             CE_PLAYER_COLLECTS_X);
13363
13364         CheckTriggeredElementChangeByPlayer(jx, jy, last_element, change_event,
13365                                             player->index_bit, enter_side);
13366
13367         player->last_removed_element = EL_UNDEFINED;
13368       }
13369
13370       if (!player->active)
13371         RemovePlayer(player);
13372     }
13373
13374     if (level.use_step_counter)
13375       CheckLevelTime_StepCounter();
13376
13377     if (tape.single_step && tape.recording && !tape.pausing &&
13378         !player->programmed_action)
13379       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13380
13381     if (!player->programmed_action)
13382       CheckSaveEngineSnapshot(player);
13383   }
13384 }
13385
13386 void ScrollScreen(struct PlayerInfo *player, int mode)
13387 {
13388   static DelayCounter screen_frame_counter = { 0 };
13389
13390   if (mode == SCROLL_INIT)
13391   {
13392     // set scrolling step size according to actual player's moving speed
13393     ScrollStepSize = TILEX / player->move_delay_value;
13394
13395     screen_frame_counter.count = FrameCounter;
13396     screen_frame_counter.value = 1;
13397
13398     ScreenMovDir = player->MovDir;
13399     ScreenMovPos = player->MovPos;
13400     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13401     return;
13402   }
13403   else if (!FrameReached(&screen_frame_counter))
13404     return;
13405
13406   if (ScreenMovPos)
13407   {
13408     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13409     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13410     redraw_mask |= REDRAW_FIELD;
13411   }
13412   else
13413     ScreenMovDir = MV_NONE;
13414 }
13415
13416 void CheckNextToConditions(int x, int y)
13417 {
13418   int element = Tile[x][y];
13419
13420   if (IS_PLAYER(x, y))
13421     TestIfPlayerNextToCustomElement(x, y);
13422
13423   if (CAN_CHANGE_OR_HAS_ACTION(element) &&
13424       HAS_ANY_CHANGE_EVENT(element, CE_NEXT_TO_X))
13425     TestIfElementNextToCustomElement(x, y);
13426 }
13427
13428 void TestIfPlayerNextToCustomElement(int x, int y)
13429 {
13430   struct XY *xy = xy_topdown;
13431   static int trigger_sides[4][2] =
13432   {
13433     // center side       border side
13434     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13435     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13436     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13437     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13438   };
13439   int i;
13440
13441   if (!IS_PLAYER(x, y))
13442     return;
13443
13444   struct PlayerInfo *player = PLAYERINFO(x, y);
13445
13446   if (player->is_moving)
13447     return;
13448
13449   for (i = 0; i < NUM_DIRECTIONS; i++)
13450   {
13451     int xx = x + xy[i].x;
13452     int yy = y + xy[i].y;
13453     int border_side = trigger_sides[i][1];
13454     int border_element;
13455
13456     if (!IN_LEV_FIELD(xx, yy))
13457       continue;
13458
13459     if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
13460       continue;         // center and border element not connected
13461
13462     border_element = Tile[xx][yy];
13463
13464     CheckElementChangeByPlayer(xx, yy, border_element, CE_NEXT_TO_PLAYER,
13465                                player->index_bit, border_side);
13466     CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13467                                         CE_PLAYER_NEXT_TO_X,
13468                                         player->index_bit, border_side);
13469
13470     /* use player element that is initially defined in the level playfield,
13471        not the player element that corresponds to the runtime player number
13472        (example: a level that contains EL_PLAYER_3 as the only player would
13473        incorrectly give EL_PLAYER_1 for "player->element_nr") */
13474
13475     CheckElementChangeBySide(xx, yy, border_element, player->initial_element,
13476                              CE_NEXT_TO_X, border_side);
13477   }
13478 }
13479
13480 void TestIfPlayerTouchesCustomElement(int x, int y)
13481 {
13482   struct XY *xy = xy_topdown;
13483   static int trigger_sides[4][2] =
13484   {
13485     // center side       border side
13486     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13487     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13488     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13489     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13490   };
13491   static int touch_dir[4] =
13492   {
13493     MV_LEFT | MV_RIGHT,
13494     MV_UP   | MV_DOWN,
13495     MV_UP   | MV_DOWN,
13496     MV_LEFT | MV_RIGHT
13497   };
13498   int center_element = Tile[x][y];      // should always be non-moving!
13499   int i;
13500
13501   for (i = 0; i < NUM_DIRECTIONS; i++)
13502   {
13503     int xx = x + xy[i].x;
13504     int yy = y + xy[i].y;
13505     int center_side = trigger_sides[i][0];
13506     int border_side = trigger_sides[i][1];
13507     int border_element;
13508
13509     if (!IN_LEV_FIELD(xx, yy))
13510       continue;
13511
13512     if (IS_PLAYER(x, y))                // player found at center element
13513     {
13514       struct PlayerInfo *player = PLAYERINFO(x, y);
13515
13516       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13517         border_element = Tile[xx][yy];          // may be moving!
13518       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13519         border_element = Tile[xx][yy];
13520       else if (MovDir[xx][yy] & touch_dir[i])   // elements are touching
13521         border_element = MovingOrBlocked2Element(xx, yy);
13522       else
13523         continue;               // center and border element do not touch
13524
13525       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13526                                  player->index_bit, border_side);
13527       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13528                                           CE_PLAYER_TOUCHES_X,
13529                                           player->index_bit, border_side);
13530
13531       {
13532         /* use player element that is initially defined in the level playfield,
13533            not the player element that corresponds to the runtime player number
13534            (example: a level that contains EL_PLAYER_3 as the only player would
13535            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13536         int player_element = PLAYERINFO(x, y)->initial_element;
13537
13538         // as element "X" is the player here, check opposite (center) side
13539         CheckElementChangeBySide(xx, yy, border_element, player_element,
13540                                  CE_TOUCHING_X, center_side);
13541       }
13542     }
13543     else if (IS_PLAYER(xx, yy))         // player found at border element
13544     {
13545       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13546
13547       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13548       {
13549         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13550           continue;             // center and border element do not touch
13551       }
13552
13553       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13554                                  player->index_bit, center_side);
13555       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13556                                           CE_PLAYER_TOUCHES_X,
13557                                           player->index_bit, center_side);
13558
13559       {
13560         /* use player element that is initially defined in the level playfield,
13561            not the player element that corresponds to the runtime player number
13562            (example: a level that contains EL_PLAYER_3 as the only player would
13563            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13564         int player_element = PLAYERINFO(xx, yy)->initial_element;
13565
13566         // as element "X" is the player here, check opposite (border) side
13567         CheckElementChangeBySide(x, y, center_element, player_element,
13568                                  CE_TOUCHING_X, border_side);
13569       }
13570
13571       break;
13572     }
13573   }
13574 }
13575
13576 void TestIfElementNextToCustomElement(int x, int y)
13577 {
13578   struct XY *xy = xy_topdown;
13579   static int trigger_sides[4][2] =
13580   {
13581     // center side      border side
13582     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13583     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13584     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13585     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13586   };
13587   int center_element = Tile[x][y];      // should always be non-moving!
13588   int i;
13589
13590   if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
13591     return;
13592
13593   for (i = 0; i < NUM_DIRECTIONS; i++)
13594   {
13595     int xx = x + xy[i].x;
13596     int yy = y + xy[i].y;
13597     int border_side = trigger_sides[i][1];
13598     int border_element;
13599
13600     if (!IN_LEV_FIELD(xx, yy))
13601       continue;
13602
13603     if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
13604       continue;                 // center and border element not connected
13605
13606     border_element = Tile[xx][yy];
13607
13608     // check for change of center element (but change it only once)
13609     if (CheckElementChangeBySide(x, y, center_element, border_element,
13610                                  CE_NEXT_TO_X, border_side))
13611       break;
13612   }
13613 }
13614
13615 void TestIfElementTouchesCustomElement(int x, int y)
13616 {
13617   struct XY *xy = xy_topdown;
13618   static int trigger_sides[4][2] =
13619   {
13620     // center side      border side
13621     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13622     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13623     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13624     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13625   };
13626   static int touch_dir[4] =
13627   {
13628     MV_LEFT | MV_RIGHT,
13629     MV_UP   | MV_DOWN,
13630     MV_UP   | MV_DOWN,
13631     MV_LEFT | MV_RIGHT
13632   };
13633   boolean change_center_element = FALSE;
13634   int center_element = Tile[x][y];      // should always be non-moving!
13635   int border_element_old[NUM_DIRECTIONS];
13636   int i;
13637
13638   for (i = 0; i < NUM_DIRECTIONS; i++)
13639   {
13640     int xx = x + xy[i].x;
13641     int yy = y + xy[i].y;
13642     int border_element;
13643
13644     border_element_old[i] = -1;
13645
13646     if (!IN_LEV_FIELD(xx, yy))
13647       continue;
13648
13649     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13650       border_element = Tile[xx][yy];    // may be moving!
13651     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13652       border_element = Tile[xx][yy];
13653     else if (MovDir[xx][yy] & touch_dir[i])     // elements are touching
13654       border_element = MovingOrBlocked2Element(xx, yy);
13655     else
13656       continue;                 // center and border element do not touch
13657
13658     border_element_old[i] = border_element;
13659   }
13660
13661   for (i = 0; i < NUM_DIRECTIONS; i++)
13662   {
13663     int xx = x + xy[i].x;
13664     int yy = y + xy[i].y;
13665     int center_side = trigger_sides[i][0];
13666     int border_element = border_element_old[i];
13667
13668     if (border_element == -1)
13669       continue;
13670
13671     // check for change of border element
13672     CheckElementChangeBySide(xx, yy, border_element, center_element,
13673                              CE_TOUCHING_X, center_side);
13674
13675     // (center element cannot be player, so we don't have to check this here)
13676   }
13677
13678   for (i = 0; i < NUM_DIRECTIONS; i++)
13679   {
13680     int xx = x + xy[i].x;
13681     int yy = y + xy[i].y;
13682     int border_side = trigger_sides[i][1];
13683     int border_element = border_element_old[i];
13684
13685     if (border_element == -1)
13686       continue;
13687
13688     // check for change of center element (but change it only once)
13689     if (!change_center_element)
13690       change_center_element =
13691         CheckElementChangeBySide(x, y, center_element, border_element,
13692                                  CE_TOUCHING_X, border_side);
13693
13694     if (IS_PLAYER(xx, yy))
13695     {
13696       /* use player element that is initially defined in the level playfield,
13697          not the player element that corresponds to the runtime player number
13698          (example: a level that contains EL_PLAYER_3 as the only player would
13699          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13700       int player_element = PLAYERINFO(xx, yy)->initial_element;
13701
13702       // as element "X" is the player here, check opposite (border) side
13703       CheckElementChangeBySide(x, y, center_element, player_element,
13704                                CE_TOUCHING_X, border_side);
13705     }
13706   }
13707 }
13708
13709 void TestIfElementHitsCustomElement(int x, int y, int direction)
13710 {
13711   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13712   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13713   int hitx = x + dx, hity = y + dy;
13714   int hitting_element = Tile[x][y];
13715   int touched_element;
13716
13717   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13718     return;
13719
13720   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13721                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13722
13723   if (IN_LEV_FIELD(hitx, hity))
13724   {
13725     int opposite_direction = MV_DIR_OPPOSITE(direction);
13726     int hitting_side = direction;
13727     int touched_side = opposite_direction;
13728     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13729                           MovDir[hitx][hity] != direction ||
13730                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13731
13732     object_hit = TRUE;
13733
13734     if (object_hit)
13735     {
13736       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13737                                CE_HITTING_X, touched_side);
13738
13739       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13740                                CE_HIT_BY_X, hitting_side);
13741
13742       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13743                                CE_HIT_BY_SOMETHING, opposite_direction);
13744
13745       if (IS_PLAYER(hitx, hity))
13746       {
13747         /* use player element that is initially defined in the level playfield,
13748            not the player element that corresponds to the runtime player number
13749            (example: a level that contains EL_PLAYER_3 as the only player would
13750            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13751         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13752
13753         CheckElementChangeBySide(x, y, hitting_element, player_element,
13754                                  CE_HITTING_X, touched_side);
13755       }
13756     }
13757   }
13758
13759   // "hitting something" is also true when hitting the playfield border
13760   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13761                            CE_HITTING_SOMETHING, direction);
13762 }
13763
13764 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13765 {
13766   int i, kill_x = -1, kill_y = -1;
13767
13768   int bad_element = -1;
13769   struct XY *test_xy = xy_topdown;
13770   static int test_dir[4] =
13771   {
13772     MV_UP,
13773     MV_LEFT,
13774     MV_RIGHT,
13775     MV_DOWN
13776   };
13777
13778   for (i = 0; i < NUM_DIRECTIONS; i++)
13779   {
13780     int test_x, test_y, test_move_dir, test_element;
13781
13782     test_x = good_x + test_xy[i].x;
13783     test_y = good_y + test_xy[i].y;
13784
13785     if (!IN_LEV_FIELD(test_x, test_y))
13786       continue;
13787
13788     test_move_dir =
13789       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13790
13791     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13792
13793     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13794        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13795     */
13796     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13797         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13798     {
13799       kill_x = test_x;
13800       kill_y = test_y;
13801       bad_element = test_element;
13802
13803       break;
13804     }
13805   }
13806
13807   if (kill_x != -1 || kill_y != -1)
13808   {
13809     if (IS_PLAYER(good_x, good_y))
13810     {
13811       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13812
13813       if (player->shield_deadly_time_left > 0 &&
13814           !IS_INDESTRUCTIBLE(bad_element))
13815         Bang(kill_x, kill_y);
13816       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13817         KillPlayer(player);
13818     }
13819     else
13820       Bang(good_x, good_y);
13821   }
13822 }
13823
13824 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13825 {
13826   int i, kill_x = -1, kill_y = -1;
13827   int bad_element = Tile[bad_x][bad_y];
13828   struct XY *test_xy = xy_topdown;
13829   static int touch_dir[4] =
13830   {
13831     MV_LEFT | MV_RIGHT,
13832     MV_UP   | MV_DOWN,
13833     MV_UP   | MV_DOWN,
13834     MV_LEFT | MV_RIGHT
13835   };
13836   static int test_dir[4] =
13837   {
13838     MV_UP,
13839     MV_LEFT,
13840     MV_RIGHT,
13841     MV_DOWN
13842   };
13843
13844   if (bad_element == EL_EXPLOSION)      // skip just exploding bad things
13845     return;
13846
13847   for (i = 0; i < NUM_DIRECTIONS; i++)
13848   {
13849     int test_x, test_y, test_move_dir, test_element;
13850
13851     test_x = bad_x + test_xy[i].x;
13852     test_y = bad_y + test_xy[i].y;
13853
13854     if (!IN_LEV_FIELD(test_x, test_y))
13855       continue;
13856
13857     test_move_dir =
13858       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13859
13860     test_element = Tile[test_x][test_y];
13861
13862     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13863        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13864     */
13865     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13866         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13867     {
13868       // good thing is player or penguin that does not move away
13869       if (IS_PLAYER(test_x, test_y))
13870       {
13871         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13872
13873         if (bad_element == EL_ROBOT && player->is_moving)
13874           continue;     // robot does not kill player if he is moving
13875
13876         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13877         {
13878           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13879             continue;           // center and border element do not touch
13880         }
13881
13882         kill_x = test_x;
13883         kill_y = test_y;
13884
13885         break;
13886       }
13887       else if (test_element == EL_PENGUIN)
13888       {
13889         kill_x = test_x;
13890         kill_y = test_y;
13891
13892         break;
13893       }
13894     }
13895   }
13896
13897   if (kill_x != -1 || kill_y != -1)
13898   {
13899     if (IS_PLAYER(kill_x, kill_y))
13900     {
13901       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13902
13903       if (player->shield_deadly_time_left > 0 &&
13904           !IS_INDESTRUCTIBLE(bad_element))
13905         Bang(bad_x, bad_y);
13906       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13907         KillPlayer(player);
13908     }
13909     else
13910       Bang(kill_x, kill_y);
13911   }
13912 }
13913
13914 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13915 {
13916   int bad_element = Tile[bad_x][bad_y];
13917   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13918   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13919   int test_x = bad_x + dx, test_y = bad_y + dy;
13920   int test_move_dir, test_element;
13921   int kill_x = -1, kill_y = -1;
13922
13923   if (!IN_LEV_FIELD(test_x, test_y))
13924     return;
13925
13926   test_move_dir =
13927     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13928
13929   test_element = Tile[test_x][test_y];
13930
13931   if (test_move_dir != bad_move_dir)
13932   {
13933     // good thing can be player or penguin that does not move away
13934     if (IS_PLAYER(test_x, test_y))
13935     {
13936       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13937
13938       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13939          player as being hit when he is moving towards the bad thing, because
13940          the "get hit by" condition would be lost after the player stops) */
13941       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13942         return;         // player moves away from bad thing
13943
13944       kill_x = test_x;
13945       kill_y = test_y;
13946     }
13947     else if (test_element == EL_PENGUIN)
13948     {
13949       kill_x = test_x;
13950       kill_y = test_y;
13951     }
13952   }
13953
13954   if (kill_x != -1 || kill_y != -1)
13955   {
13956     if (IS_PLAYER(kill_x, kill_y))
13957     {
13958       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13959
13960       if (player->shield_deadly_time_left > 0 &&
13961           !IS_INDESTRUCTIBLE(bad_element))
13962         Bang(bad_x, bad_y);
13963       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13964         KillPlayer(player);
13965     }
13966     else
13967       Bang(kill_x, kill_y);
13968   }
13969 }
13970
13971 void TestIfPlayerTouchesBadThing(int x, int y)
13972 {
13973   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13974 }
13975
13976 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13977 {
13978   TestIfGoodThingHitsBadThing(x, y, move_dir);
13979 }
13980
13981 void TestIfBadThingTouchesPlayer(int x, int y)
13982 {
13983   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13984 }
13985
13986 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13987 {
13988   TestIfBadThingHitsGoodThing(x, y, move_dir);
13989 }
13990
13991 void TestIfFriendTouchesBadThing(int x, int y)
13992 {
13993   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13994 }
13995
13996 void TestIfBadThingTouchesFriend(int x, int y)
13997 {
13998   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13999 }
14000
14001 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
14002 {
14003   int i, kill_x = bad_x, kill_y = bad_y;
14004   struct XY *xy = xy_topdown;
14005
14006   for (i = 0; i < NUM_DIRECTIONS; i++)
14007   {
14008     int x, y, element;
14009
14010     x = bad_x + xy[i].x;
14011     y = bad_y + xy[i].y;
14012     if (!IN_LEV_FIELD(x, y))
14013       continue;
14014
14015     element = Tile[x][y];
14016     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
14017         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
14018     {
14019       kill_x = x;
14020       kill_y = y;
14021       break;
14022     }
14023   }
14024
14025   if (kill_x != bad_x || kill_y != bad_y)
14026     Bang(bad_x, bad_y);
14027 }
14028
14029 void KillPlayer(struct PlayerInfo *player)
14030 {
14031   int jx = player->jx, jy = player->jy;
14032
14033   if (!player->active)
14034     return;
14035
14036 #if 0
14037   Debug("game:playing:KillPlayer",
14038         "0: killed == %d, active == %d, reanimated == %d",
14039         player->killed, player->active, player->reanimated);
14040 #endif
14041
14042   /* the following code was introduced to prevent an infinite loop when calling
14043      -> Bang()
14044      -> CheckTriggeredElementChangeExt()
14045      -> ExecuteCustomElementAction()
14046      -> KillPlayer()
14047      -> (infinitely repeating the above sequence of function calls)
14048      which occurs when killing the player while having a CE with the setting
14049      "kill player X when explosion of <player X>"; the solution using a new
14050      field "player->killed" was chosen for backwards compatibility, although
14051      clever use of the fields "player->active" etc. would probably also work */
14052 #if 1
14053   if (player->killed)
14054     return;
14055 #endif
14056
14057   player->killed = TRUE;
14058
14059   // remove accessible field at the player's position
14060   RemoveField(jx, jy);
14061
14062   // deactivate shield (else Bang()/Explode() would not work right)
14063   player->shield_normal_time_left = 0;
14064   player->shield_deadly_time_left = 0;
14065
14066 #if 0
14067   Debug("game:playing:KillPlayer",
14068         "1: killed == %d, active == %d, reanimated == %d",
14069         player->killed, player->active, player->reanimated);
14070 #endif
14071
14072   Bang(jx, jy);
14073
14074 #if 0
14075   Debug("game:playing:KillPlayer",
14076         "2: killed == %d, active == %d, reanimated == %d",
14077         player->killed, player->active, player->reanimated);
14078 #endif
14079
14080   if (player->reanimated)       // killed player may have been reanimated
14081     player->killed = player->reanimated = FALSE;
14082   else
14083     BuryPlayer(player);
14084 }
14085
14086 static void KillPlayerUnlessEnemyProtected(int x, int y)
14087 {
14088   if (!PLAYER_ENEMY_PROTECTED(x, y))
14089     KillPlayer(PLAYERINFO(x, y));
14090 }
14091
14092 static void KillPlayerUnlessExplosionProtected(int x, int y)
14093 {
14094   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14095     KillPlayer(PLAYERINFO(x, y));
14096 }
14097
14098 void BuryPlayer(struct PlayerInfo *player)
14099 {
14100   int jx = player->jx, jy = player->jy;
14101
14102   if (!player->active)
14103     return;
14104
14105   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14106
14107   RemovePlayer(player);
14108
14109   player->buried = TRUE;
14110
14111   if (game.all_players_gone)
14112     game.GameOver = TRUE;
14113 }
14114
14115 void RemovePlayer(struct PlayerInfo *player)
14116 {
14117   int jx = player->jx, jy = player->jy;
14118   int i, found = FALSE;
14119
14120   player->present = FALSE;
14121   player->active = FALSE;
14122
14123   // required for some CE actions (even if the player is not active anymore)
14124   player->MovPos = 0;
14125
14126   if (!ExplodeField[jx][jy])
14127     StorePlayer[jx][jy] = 0;
14128
14129   if (player->is_moving)
14130     TEST_DrawLevelField(player->last_jx, player->last_jy);
14131
14132   for (i = 0; i < MAX_PLAYERS; i++)
14133     if (stored_player[i].active)
14134       found = TRUE;
14135
14136   if (!found)
14137   {
14138     game.all_players_gone = TRUE;
14139     game.GameOver = TRUE;
14140   }
14141
14142   game.exit_x = game.robot_wheel_x = jx;
14143   game.exit_y = game.robot_wheel_y = jy;
14144 }
14145
14146 void ExitPlayer(struct PlayerInfo *player)
14147 {
14148   DrawPlayer(player);   // needed here only to cleanup last field
14149   RemovePlayer(player);
14150
14151   if (game.players_still_needed > 0)
14152     game.players_still_needed--;
14153 }
14154
14155 static void SetFieldForSnapping(int x, int y, int element, int direction,
14156                                 int player_index_bit)
14157 {
14158   struct ElementInfo *ei = &element_info[element];
14159   int direction_bit = MV_DIR_TO_BIT(direction);
14160   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14161   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14162                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14163
14164   Tile[x][y] = EL_ELEMENT_SNAPPING;
14165   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14166   MovDir[x][y] = direction;
14167   Store[x][y] = element;
14168   Store2[x][y] = player_index_bit;
14169
14170   ResetGfxAnimation(x, y);
14171
14172   GfxElement[x][y] = element;
14173   GfxAction[x][y] = action;
14174   GfxDir[x][y] = direction;
14175   GfxFrame[x][y] = -1;
14176 }
14177
14178 static void TestFieldAfterSnapping(int x, int y, int element, int direction,
14179                                    int player_index_bit)
14180 {
14181   TestIfElementTouchesCustomElement(x, y);      // for empty space
14182
14183   if (level.finish_dig_collect)
14184   {
14185     int dig_side = MV_DIR_OPPOSITE(direction);
14186     int change_event = (IS_DIGGABLE(element) ? CE_PLAYER_DIGS_X :
14187                         CE_PLAYER_COLLECTS_X);
14188
14189     CheckTriggeredElementChangeByPlayer(x, y, element, change_event,
14190                                         player_index_bit, dig_side);
14191     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14192                                         player_index_bit, dig_side);
14193   }
14194 }
14195
14196 /*
14197   =============================================================================
14198   checkDiagonalPushing()
14199   -----------------------------------------------------------------------------
14200   check if diagonal input device direction results in pushing of object
14201   (by checking if the alternative direction is walkable, diggable, ...)
14202   =============================================================================
14203 */
14204
14205 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14206                                     int x, int y, int real_dx, int real_dy)
14207 {
14208   int jx, jy, dx, dy, xx, yy;
14209
14210   if (real_dx == 0 || real_dy == 0)     // no diagonal direction => push
14211     return TRUE;
14212
14213   // diagonal direction: check alternative direction
14214   jx = player->jx;
14215   jy = player->jy;
14216   dx = x - jx;
14217   dy = y - jy;
14218   xx = jx + (dx == 0 ? real_dx : 0);
14219   yy = jy + (dy == 0 ? real_dy : 0);
14220
14221   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Tile[xx][yy]));
14222 }
14223
14224 /*
14225   =============================================================================
14226   DigField()
14227   -----------------------------------------------------------------------------
14228   x, y:                 field next to player (non-diagonal) to try to dig to
14229   real_dx, real_dy:     direction as read from input device (can be diagonal)
14230   =============================================================================
14231 */
14232
14233 static int DigField(struct PlayerInfo *player,
14234                     int oldx, int oldy, int x, int y,
14235                     int real_dx, int real_dy, int mode)
14236 {
14237   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14238   boolean player_was_pushing = player->is_pushing;
14239   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14240   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14241   int jx = oldx, jy = oldy;
14242   int dx = x - jx, dy = y - jy;
14243   int nextx = x + dx, nexty = y + dy;
14244   int move_direction = (dx == -1 ? MV_LEFT  :
14245                         dx == +1 ? MV_RIGHT :
14246                         dy == -1 ? MV_UP    :
14247                         dy == +1 ? MV_DOWN  : MV_NONE);
14248   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14249   int dig_side = MV_DIR_OPPOSITE(move_direction);
14250   int old_element = Tile[jx][jy];
14251   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14252   int collect_count;
14253
14254   if (is_player)                // function can also be called by EL_PENGUIN
14255   {
14256     if (player->MovPos == 0)
14257     {
14258       player->is_digging = FALSE;
14259       player->is_collecting = FALSE;
14260     }
14261
14262     if (player->MovPos == 0)    // last pushing move finished
14263       player->is_pushing = FALSE;
14264
14265     if (mode == DF_NO_PUSH)     // player just stopped pushing
14266     {
14267       player->is_switching = FALSE;
14268       player->push_delay = -1;
14269
14270       return MP_NO_ACTION;
14271     }
14272   }
14273   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14274     old_element = Back[jx][jy];
14275
14276   // in case of element dropped at player position, check background
14277   else if (Back[jx][jy] != EL_EMPTY &&
14278            game.engine_version >= VERSION_IDENT(2,2,0,0))
14279     old_element = Back[jx][jy];
14280
14281   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14282     return MP_NO_ACTION;        // field has no opening in this direction
14283
14284   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element, opposite_direction))
14285     return MP_NO_ACTION;        // field has no opening in this direction
14286
14287   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14288   {
14289     SplashAcid(x, y);
14290
14291     Tile[jx][jy] = player->artwork_element;
14292     InitMovingField(jx, jy, MV_DOWN);
14293     Store[jx][jy] = EL_ACID;
14294     ContinueMoving(jx, jy);
14295     BuryPlayer(player);
14296
14297     return MP_DONT_RUN_INTO;
14298   }
14299
14300   if (player_can_move && DONT_RUN_INTO(element))
14301   {
14302     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14303
14304     return MP_DONT_RUN_INTO;
14305   }
14306
14307   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14308     return MP_NO_ACTION;
14309
14310   collect_count = element_info[element].collect_count_initial;
14311
14312   if (!is_player && !IS_COLLECTIBLE(element))   // penguin cannot collect it
14313     return MP_NO_ACTION;
14314
14315   if (game.engine_version < VERSION_IDENT(2,2,0,0))
14316     player_can_move = player_can_move_or_snap;
14317
14318   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14319       game.engine_version >= VERSION_IDENT(2,2,0,0))
14320   {
14321     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14322                                player->index_bit, dig_side);
14323     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14324                                         player->index_bit, dig_side);
14325
14326     if (element == EL_DC_LANDMINE)
14327       Bang(x, y);
14328
14329     if (Tile[x][y] != element)          // field changed by snapping
14330       return MP_ACTION;
14331
14332     return MP_NO_ACTION;
14333   }
14334
14335   if (player->gravity && is_player && !player->is_auto_moving &&
14336       canFallDown(player) && move_direction != MV_DOWN &&
14337       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14338     return MP_NO_ACTION;        // player cannot walk here due to gravity
14339
14340   if (player_can_move &&
14341       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14342   {
14343     int sound_element = SND_ELEMENT(element);
14344     int sound_action = ACTION_WALKING;
14345
14346     if (IS_RND_GATE(element))
14347     {
14348       if (!player->key[RND_GATE_NR(element)])
14349         return MP_NO_ACTION;
14350     }
14351     else if (IS_RND_GATE_GRAY(element))
14352     {
14353       if (!player->key[RND_GATE_GRAY_NR(element)])
14354         return MP_NO_ACTION;
14355     }
14356     else if (IS_RND_GATE_GRAY_ACTIVE(element))
14357     {
14358       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14359         return MP_NO_ACTION;
14360     }
14361     else if (element == EL_EXIT_OPEN ||
14362              element == EL_EM_EXIT_OPEN ||
14363              element == EL_EM_EXIT_OPENING ||
14364              element == EL_STEEL_EXIT_OPEN ||
14365              element == EL_EM_STEEL_EXIT_OPEN ||
14366              element == EL_EM_STEEL_EXIT_OPENING ||
14367              element == EL_SP_EXIT_OPEN ||
14368              element == EL_SP_EXIT_OPENING)
14369     {
14370       sound_action = ACTION_PASSING;    // player is passing exit
14371     }
14372     else if (element == EL_EMPTY)
14373     {
14374       sound_action = ACTION_MOVING;             // nothing to walk on
14375     }
14376
14377     // play sound from background or player, whatever is available
14378     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14379       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14380     else
14381       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14382   }
14383   else if (player_can_move &&
14384            IS_PASSABLE(element) && canPassField(x, y, move_direction))
14385   {
14386     if (!ACCESS_FROM(element, opposite_direction))
14387       return MP_NO_ACTION;      // field not accessible from this direction
14388
14389     if (CAN_MOVE(element))      // only fixed elements can be passed!
14390       return MP_NO_ACTION;
14391
14392     if (IS_EM_GATE(element))
14393     {
14394       if (!player->key[EM_GATE_NR(element)])
14395         return MP_NO_ACTION;
14396     }
14397     else if (IS_EM_GATE_GRAY(element))
14398     {
14399       if (!player->key[EM_GATE_GRAY_NR(element)])
14400         return MP_NO_ACTION;
14401     }
14402     else if (IS_EM_GATE_GRAY_ACTIVE(element))
14403     {
14404       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14405         return MP_NO_ACTION;
14406     }
14407     else if (IS_EMC_GATE(element))
14408     {
14409       if (!player->key[EMC_GATE_NR(element)])
14410         return MP_NO_ACTION;
14411     }
14412     else if (IS_EMC_GATE_GRAY(element))
14413     {
14414       if (!player->key[EMC_GATE_GRAY_NR(element)])
14415         return MP_NO_ACTION;
14416     }
14417     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14418     {
14419       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14420         return MP_NO_ACTION;
14421     }
14422     else if (element == EL_DC_GATE_WHITE ||
14423              element == EL_DC_GATE_WHITE_GRAY ||
14424              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14425     {
14426       if (player->num_white_keys == 0)
14427         return MP_NO_ACTION;
14428
14429       player->num_white_keys--;
14430     }
14431     else if (IS_SP_PORT(element))
14432     {
14433       if (element == EL_SP_GRAVITY_PORT_LEFT ||
14434           element == EL_SP_GRAVITY_PORT_RIGHT ||
14435           element == EL_SP_GRAVITY_PORT_UP ||
14436           element == EL_SP_GRAVITY_PORT_DOWN)
14437         player->gravity = !player->gravity;
14438       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14439                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14440                element == EL_SP_GRAVITY_ON_PORT_UP ||
14441                element == EL_SP_GRAVITY_ON_PORT_DOWN)
14442         player->gravity = TRUE;
14443       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14444                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14445                element == EL_SP_GRAVITY_OFF_PORT_UP ||
14446                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14447         player->gravity = FALSE;
14448     }
14449
14450     // automatically move to the next field with double speed
14451     player->programmed_action = move_direction;
14452
14453     if (player->move_delay_reset_counter == 0)
14454     {
14455       player->move_delay_reset_counter = 2;     // two double speed steps
14456
14457       DOUBLE_PLAYER_SPEED(player);
14458     }
14459
14460     PlayLevelSoundAction(x, y, ACTION_PASSING);
14461   }
14462   else if (player_can_move_or_snap && IS_DIGGABLE(element))
14463   {
14464     RemoveField(x, y);
14465
14466     if (mode != DF_SNAP)
14467     {
14468       GfxElement[x][y] = GFX_ELEMENT(element);
14469       player->is_digging = TRUE;
14470     }
14471
14472     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14473
14474     // use old behaviour for old levels (digging)
14475     if (!level.finish_dig_collect)
14476     {
14477       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14478                                           player->index_bit, dig_side);
14479
14480       // if digging triggered player relocation, finish digging tile
14481       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14482         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14483     }
14484
14485     if (mode == DF_SNAP)
14486     {
14487       if (level.block_snap_field)
14488         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14489       else
14490         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14491
14492       // use old behaviour for old levels (snapping)
14493       if (!level.finish_dig_collect)
14494         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14495                                             player->index_bit, dig_side);
14496     }
14497   }
14498   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14499   {
14500     RemoveField(x, y);
14501
14502     if (is_player && mode != DF_SNAP)
14503     {
14504       GfxElement[x][y] = element;
14505       player->is_collecting = TRUE;
14506     }
14507
14508     if (element == EL_SPEED_PILL)
14509     {
14510       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14511     }
14512     else if (element == EL_EXTRA_TIME && level.time > 0)
14513     {
14514       TimeLeft += level.extra_time;
14515
14516       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14517
14518       DisplayGameControlValues();
14519     }
14520     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14521     {
14522       int shield_time = (element == EL_SHIELD_DEADLY ?
14523                          level.shield_deadly_time :
14524                          level.shield_normal_time);
14525
14526       player->shield_normal_time_left += shield_time;
14527       if (element == EL_SHIELD_DEADLY)
14528         player->shield_deadly_time_left += shield_time;
14529     }
14530     else if (element == EL_DYNAMITE ||
14531              element == EL_EM_DYNAMITE ||
14532              element == EL_SP_DISK_RED)
14533     {
14534       if (player->inventory_size < MAX_INVENTORY_SIZE)
14535         player->inventory_element[player->inventory_size++] = element;
14536
14537       DrawGameDoorValues();
14538     }
14539     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14540     {
14541       player->dynabomb_count++;
14542       player->dynabombs_left++;
14543     }
14544     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14545     {
14546       player->dynabomb_size++;
14547     }
14548     else if (element == EL_DYNABOMB_INCREASE_POWER)
14549     {
14550       player->dynabomb_xl = TRUE;
14551     }
14552     else if (IS_KEY(element))
14553     {
14554       player->key[KEY_NR(element)] = TRUE;
14555
14556       DrawGameDoorValues();
14557     }
14558     else if (element == EL_DC_KEY_WHITE)
14559     {
14560       player->num_white_keys++;
14561
14562       // display white keys?
14563       // DrawGameDoorValues();
14564     }
14565     else if (IS_ENVELOPE(element))
14566     {
14567       boolean wait_for_snapping = (mode == DF_SNAP && level.block_snap_field);
14568
14569       if (!wait_for_snapping)
14570         player->show_envelope = element;
14571     }
14572     else if (element == EL_EMC_LENSES)
14573     {
14574       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14575
14576       RedrawAllInvisibleElementsForLenses();
14577     }
14578     else if (element == EL_EMC_MAGNIFIER)
14579     {
14580       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14581
14582       RedrawAllInvisibleElementsForMagnifier();
14583     }
14584     else if (IS_DROPPABLE(element) ||
14585              IS_THROWABLE(element))     // can be collected and dropped
14586     {
14587       int i;
14588
14589       if (collect_count == 0)
14590         player->inventory_infinite_element = element;
14591       else
14592         for (i = 0; i < collect_count; i++)
14593           if (player->inventory_size < MAX_INVENTORY_SIZE)
14594             player->inventory_element[player->inventory_size++] = element;
14595
14596       DrawGameDoorValues();
14597     }
14598     else if (collect_count > 0)
14599     {
14600       game.gems_still_needed -= collect_count;
14601       if (game.gems_still_needed < 0)
14602         game.gems_still_needed = 0;
14603
14604       game.snapshot.collected_item = TRUE;
14605
14606       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
14607
14608       DisplayGameControlValues();
14609     }
14610
14611     RaiseScoreElement(element);
14612     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14613
14614     // use old behaviour for old levels (collecting)
14615     if (!level.finish_dig_collect && is_player)
14616     {
14617       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14618                                           player->index_bit, dig_side);
14619
14620       // if collecting triggered player relocation, finish collecting tile
14621       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14622         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14623     }
14624
14625     if (mode == DF_SNAP)
14626     {
14627       if (level.block_snap_field)
14628         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14629       else
14630         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14631
14632       // use old behaviour for old levels (snapping)
14633       if (!level.finish_dig_collect)
14634         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14635                                             player->index_bit, dig_side);
14636     }
14637   }
14638   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14639   {
14640     if (mode == DF_SNAP && element != EL_BD_ROCK)
14641       return MP_NO_ACTION;
14642
14643     if (CAN_FALL(element) && dy)
14644       return MP_NO_ACTION;
14645
14646     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14647         !(element == EL_SPRING && level.use_spring_bug))
14648       return MP_NO_ACTION;
14649
14650     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14651         ((move_direction & MV_VERTICAL &&
14652           ((element_info[element].move_pattern & MV_LEFT &&
14653             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14654            (element_info[element].move_pattern & MV_RIGHT &&
14655             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14656          (move_direction & MV_HORIZONTAL &&
14657           ((element_info[element].move_pattern & MV_UP &&
14658             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14659            (element_info[element].move_pattern & MV_DOWN &&
14660             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14661       return MP_NO_ACTION;
14662
14663     // do not push elements already moving away faster than player
14664     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14665         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14666       return MP_NO_ACTION;
14667
14668     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14669     {
14670       if (player->push_delay_value == -1 || !player_was_pushing)
14671         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14672     }
14673     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14674     {
14675       if (player->push_delay_value == -1)
14676         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14677     }
14678     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14679     {
14680       if (!player->is_pushing)
14681         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14682     }
14683
14684     player->is_pushing = TRUE;
14685     player->is_active = TRUE;
14686
14687     if (!(IN_LEV_FIELD(nextx, nexty) &&
14688           (IS_FREE(nextx, nexty) ||
14689            (IS_SB_ELEMENT(element) &&
14690             Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14691            (IS_CUSTOM_ELEMENT(element) &&
14692             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
14693       return MP_NO_ACTION;
14694
14695     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14696       return MP_NO_ACTION;
14697
14698     if (player->push_delay == -1)       // new pushing; restart delay
14699       player->push_delay = 0;
14700
14701     if (player->push_delay < player->push_delay_value &&
14702         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14703         element != EL_SPRING && element != EL_BALLOON)
14704     {
14705       // make sure that there is no move delay before next try to push
14706       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14707         player->move_delay = 0;
14708
14709       return MP_NO_ACTION;
14710     }
14711
14712     if (IS_CUSTOM_ELEMENT(element) &&
14713         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14714     {
14715       if (!DigFieldByCE(nextx, nexty, element))
14716         return MP_NO_ACTION;
14717     }
14718
14719     if (IS_SB_ELEMENT(element))
14720     {
14721       boolean sokoban_task_solved = FALSE;
14722
14723       if (element == EL_SOKOBAN_FIELD_FULL)
14724       {
14725         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14726
14727         IncrementSokobanFieldsNeeded();
14728         IncrementSokobanObjectsNeeded();
14729       }
14730
14731       if (Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14732       {
14733         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14734
14735         DecrementSokobanFieldsNeeded();
14736         DecrementSokobanObjectsNeeded();
14737
14738         // sokoban object was pushed from empty field to sokoban field
14739         if (Back[x][y] == EL_EMPTY)
14740           sokoban_task_solved = TRUE;
14741       }
14742
14743       Tile[x][y] = EL_SOKOBAN_OBJECT;
14744
14745       if (Back[x][y] == Back[nextx][nexty])
14746         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14747       else if (Back[x][y] != 0)
14748         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14749                                     ACTION_EMPTYING);
14750       else
14751         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14752                                     ACTION_FILLING);
14753
14754       if (sokoban_task_solved &&
14755           game.sokoban_fields_still_needed == 0 &&
14756           game.sokoban_objects_still_needed == 0 &&
14757           level.auto_exit_sokoban)
14758       {
14759         game.players_still_needed = 0;
14760
14761         LevelSolved();
14762
14763         PlaySound(SND_GAME_SOKOBAN_SOLVING);
14764       }
14765     }
14766     else
14767       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14768
14769     InitMovingField(x, y, move_direction);
14770     GfxAction[x][y] = ACTION_PUSHING;
14771
14772     if (mode == DF_SNAP)
14773       ContinueMoving(x, y);
14774     else
14775       MovPos[x][y] = (dx != 0 ? dx : dy);
14776
14777     Pushed[x][y] = TRUE;
14778     Pushed[nextx][nexty] = TRUE;
14779
14780     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14781       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14782     else
14783       player->push_delay_value = -1;    // get new value later
14784
14785     // check for element change _after_ element has been pushed
14786     if (game.use_change_when_pushing_bug)
14787     {
14788       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14789                                  player->index_bit, dig_side);
14790       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14791                                           player->index_bit, dig_side);
14792     }
14793   }
14794   else if (IS_SWITCHABLE(element))
14795   {
14796     if (PLAYER_SWITCHING(player, x, y))
14797     {
14798       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14799                                           player->index_bit, dig_side);
14800
14801       return MP_ACTION;
14802     }
14803
14804     player->is_switching = TRUE;
14805     player->switch_x = x;
14806     player->switch_y = y;
14807
14808     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14809
14810     if (element == EL_ROBOT_WHEEL)
14811     {
14812       Tile[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14813
14814       game.robot_wheel_x = x;
14815       game.robot_wheel_y = y;
14816       game.robot_wheel_active = TRUE;
14817
14818       TEST_DrawLevelField(x, y);
14819     }
14820     else if (element == EL_SP_TERMINAL)
14821     {
14822       int xx, yy;
14823
14824       SCAN_PLAYFIELD(xx, yy)
14825       {
14826         if (Tile[xx][yy] == EL_SP_DISK_YELLOW)
14827         {
14828           Bang(xx, yy);
14829         }
14830         else if (Tile[xx][yy] == EL_SP_TERMINAL)
14831         {
14832           Tile[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14833
14834           ResetGfxAnimation(xx, yy);
14835           TEST_DrawLevelField(xx, yy);
14836         }
14837       }
14838     }
14839     else if (IS_BELT_SWITCH(element))
14840     {
14841       ToggleBeltSwitch(x, y);
14842     }
14843     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14844              element == EL_SWITCHGATE_SWITCH_DOWN ||
14845              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14846              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14847     {
14848       ToggleSwitchgateSwitch();
14849     }
14850     else if (element == EL_LIGHT_SWITCH ||
14851              element == EL_LIGHT_SWITCH_ACTIVE)
14852     {
14853       ToggleLightSwitch(x, y);
14854     }
14855     else if (element == EL_TIMEGATE_SWITCH ||
14856              element == EL_DC_TIMEGATE_SWITCH)
14857     {
14858       ActivateTimegateSwitch(x, y);
14859     }
14860     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14861              element == EL_BALLOON_SWITCH_RIGHT ||
14862              element == EL_BALLOON_SWITCH_UP    ||
14863              element == EL_BALLOON_SWITCH_DOWN  ||
14864              element == EL_BALLOON_SWITCH_NONE  ||
14865              element == EL_BALLOON_SWITCH_ANY)
14866     {
14867       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14868                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14869                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14870                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14871                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14872                              move_direction);
14873     }
14874     else if (element == EL_LAMP)
14875     {
14876       Tile[x][y] = EL_LAMP_ACTIVE;
14877       game.lights_still_needed--;
14878
14879       ResetGfxAnimation(x, y);
14880       TEST_DrawLevelField(x, y);
14881     }
14882     else if (element == EL_TIME_ORB_FULL)
14883     {
14884       Tile[x][y] = EL_TIME_ORB_EMPTY;
14885
14886       if (level.time > 0 || level.use_time_orb_bug)
14887       {
14888         TimeLeft += level.time_orb_time;
14889         game.no_level_time_limit = FALSE;
14890
14891         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14892
14893         DisplayGameControlValues();
14894       }
14895
14896       ResetGfxAnimation(x, y);
14897       TEST_DrawLevelField(x, y);
14898     }
14899     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14900              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14901     {
14902       int xx, yy;
14903
14904       game.ball_active = !game.ball_active;
14905
14906       SCAN_PLAYFIELD(xx, yy)
14907       {
14908         int e = Tile[xx][yy];
14909
14910         if (game.ball_active)
14911         {
14912           if (e == EL_EMC_MAGIC_BALL)
14913             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14914           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14915             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14916         }
14917         else
14918         {
14919           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14920             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14921           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14922             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14923         }
14924       }
14925     }
14926
14927     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14928                                         player->index_bit, dig_side);
14929
14930     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14931                                         player->index_bit, dig_side);
14932
14933     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14934                                         player->index_bit, dig_side);
14935
14936     return MP_ACTION;
14937   }
14938   else
14939   {
14940     if (!PLAYER_SWITCHING(player, x, y))
14941     {
14942       player->is_switching = TRUE;
14943       player->switch_x = x;
14944       player->switch_y = y;
14945
14946       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14947                                  player->index_bit, dig_side);
14948       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14949                                           player->index_bit, dig_side);
14950
14951       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14952                                  player->index_bit, dig_side);
14953       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14954                                           player->index_bit, dig_side);
14955     }
14956
14957     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14958                                player->index_bit, dig_side);
14959     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14960                                         player->index_bit, dig_side);
14961
14962     return MP_NO_ACTION;
14963   }
14964
14965   player->push_delay = -1;
14966
14967   if (is_player)                // function can also be called by EL_PENGUIN
14968   {
14969     if (Tile[x][y] != element)          // really digged/collected something
14970     {
14971       player->is_collecting = !player->is_digging;
14972       player->is_active = TRUE;
14973
14974       player->last_removed_element = element;
14975     }
14976   }
14977
14978   return MP_MOVING;
14979 }
14980
14981 static boolean DigFieldByCE(int x, int y, int digging_element)
14982 {
14983   int element = Tile[x][y];
14984
14985   if (!IS_FREE(x, y))
14986   {
14987     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14988                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14989                   ACTION_BREAKING);
14990
14991     // no element can dig solid indestructible elements
14992     if (IS_INDESTRUCTIBLE(element) &&
14993         !IS_DIGGABLE(element) &&
14994         !IS_COLLECTIBLE(element))
14995       return FALSE;
14996
14997     if (AmoebaNr[x][y] &&
14998         (element == EL_AMOEBA_FULL ||
14999          element == EL_BD_AMOEBA ||
15000          element == EL_AMOEBA_GROWING))
15001     {
15002       AmoebaCnt[AmoebaNr[x][y]]--;
15003       AmoebaCnt2[AmoebaNr[x][y]]--;
15004     }
15005
15006     if (IS_MOVING(x, y))
15007       RemoveMovingField(x, y);
15008     else
15009     {
15010       RemoveField(x, y);
15011       TEST_DrawLevelField(x, y);
15012     }
15013
15014     // if digged element was about to explode, prevent the explosion
15015     ExplodeField[x][y] = EX_TYPE_NONE;
15016
15017     PlayLevelSoundAction(x, y, action);
15018   }
15019
15020   Store[x][y] = EL_EMPTY;
15021
15022   // this makes it possible to leave the removed element again
15023   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15024     Store[x][y] = element;
15025
15026   return TRUE;
15027 }
15028
15029 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
15030 {
15031   int jx = player->jx, jy = player->jy;
15032   int x = jx + dx, y = jy + dy;
15033   int snap_direction = (dx == -1 ? MV_LEFT  :
15034                         dx == +1 ? MV_RIGHT :
15035                         dy == -1 ? MV_UP    :
15036                         dy == +1 ? MV_DOWN  : MV_NONE);
15037   boolean can_continue_snapping = (level.continuous_snapping &&
15038                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
15039
15040   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
15041     return FALSE;
15042
15043   if (!player->active || !IN_LEV_FIELD(x, y))
15044     return FALSE;
15045
15046   if (dx && dy)
15047     return FALSE;
15048
15049   if (!dx && !dy)
15050   {
15051     if (player->MovPos == 0)
15052       player->is_pushing = FALSE;
15053
15054     player->is_snapping = FALSE;
15055
15056     if (player->MovPos == 0)
15057     {
15058       player->is_moving = FALSE;
15059       player->is_digging = FALSE;
15060       player->is_collecting = FALSE;
15061     }
15062
15063     return FALSE;
15064   }
15065
15066   // prevent snapping with already pressed snap key when not allowed
15067   if (player->is_snapping && !can_continue_snapping)
15068     return FALSE;
15069
15070   player->MovDir = snap_direction;
15071
15072   if (player->MovPos == 0)
15073   {
15074     player->is_moving = FALSE;
15075     player->is_digging = FALSE;
15076     player->is_collecting = FALSE;
15077   }
15078
15079   player->is_dropping = FALSE;
15080   player->is_dropping_pressed = FALSE;
15081   player->drop_pressed_delay = 0;
15082
15083   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
15084     return FALSE;
15085
15086   player->is_snapping = TRUE;
15087   player->is_active = TRUE;
15088
15089   if (player->MovPos == 0)
15090   {
15091     player->is_moving = FALSE;
15092     player->is_digging = FALSE;
15093     player->is_collecting = FALSE;
15094   }
15095
15096   if (player->MovPos != 0)      // prevent graphic bugs in versions < 2.2.0
15097     TEST_DrawLevelField(player->last_jx, player->last_jy);
15098
15099   TEST_DrawLevelField(x, y);
15100
15101   return TRUE;
15102 }
15103
15104 static boolean DropElement(struct PlayerInfo *player)
15105 {
15106   int old_element, new_element;
15107   int dropx = player->jx, dropy = player->jy;
15108   int drop_direction = player->MovDir;
15109   int drop_side = drop_direction;
15110   int drop_element = get_next_dropped_element(player);
15111
15112   /* do not drop an element on top of another element; when holding drop key
15113      pressed without moving, dropped element must move away before the next
15114      element can be dropped (this is especially important if the next element
15115      is dynamite, which can be placed on background for historical reasons) */
15116   if (PLAYER_DROPPING(player, dropx, dropy) && Tile[dropx][dropy] != EL_EMPTY)
15117     return MP_ACTION;
15118
15119   if (IS_THROWABLE(drop_element))
15120   {
15121     dropx += GET_DX_FROM_DIR(drop_direction);
15122     dropy += GET_DY_FROM_DIR(drop_direction);
15123
15124     if (!IN_LEV_FIELD(dropx, dropy))
15125       return FALSE;
15126   }
15127
15128   old_element = Tile[dropx][dropy];     // old element at dropping position
15129   new_element = drop_element;           // default: no change when dropping
15130
15131   // check if player is active, not moving and ready to drop
15132   if (!player->active || player->MovPos || player->drop_delay > 0)
15133     return FALSE;
15134
15135   // check if player has anything that can be dropped
15136   if (new_element == EL_UNDEFINED)
15137     return FALSE;
15138
15139   // only set if player has anything that can be dropped
15140   player->is_dropping_pressed = TRUE;
15141
15142   // check if drop key was pressed long enough for EM style dynamite
15143   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15144     return FALSE;
15145
15146   // check if anything can be dropped at the current position
15147   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15148     return FALSE;
15149
15150   // collected custom elements can only be dropped on empty fields
15151   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15152     return FALSE;
15153
15154   if (old_element != EL_EMPTY)
15155     Back[dropx][dropy] = old_element;   // store old element on this field
15156
15157   ResetGfxAnimation(dropx, dropy);
15158   ResetRandomAnimationValue(dropx, dropy);
15159
15160   if (player->inventory_size > 0 ||
15161       player->inventory_infinite_element != EL_UNDEFINED)
15162   {
15163     if (player->inventory_size > 0)
15164     {
15165       player->inventory_size--;
15166
15167       DrawGameDoorValues();
15168
15169       if (new_element == EL_DYNAMITE)
15170         new_element = EL_DYNAMITE_ACTIVE;
15171       else if (new_element == EL_EM_DYNAMITE)
15172         new_element = EL_EM_DYNAMITE_ACTIVE;
15173       else if (new_element == EL_SP_DISK_RED)
15174         new_element = EL_SP_DISK_RED_ACTIVE;
15175     }
15176
15177     Tile[dropx][dropy] = new_element;
15178
15179     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15180       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15181                           el2img(Tile[dropx][dropy]), 0);
15182
15183     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15184
15185     // needed if previous element just changed to "empty" in the last frame
15186     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
15187
15188     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15189                                player->index_bit, drop_side);
15190     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15191                                         CE_PLAYER_DROPS_X,
15192                                         player->index_bit, drop_side);
15193
15194     TestIfElementTouchesCustomElement(dropx, dropy);
15195   }
15196   else          // player is dropping a dyna bomb
15197   {
15198     player->dynabombs_left--;
15199
15200     Tile[dropx][dropy] = new_element;
15201
15202     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15203       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15204                           el2img(Tile[dropx][dropy]), 0);
15205
15206     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15207   }
15208
15209   if (Tile[dropx][dropy] == new_element) // uninitialized unless CE change
15210     InitField_WithBug1(dropx, dropy, FALSE);
15211
15212   new_element = Tile[dropx][dropy];     // element might have changed
15213
15214   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
15215       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
15216   {
15217     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
15218       MovDir[dropx][dropy] = drop_direction;
15219
15220     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
15221
15222     // do not cause impact style collision by dropping elements that can fall
15223     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15224   }
15225
15226   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
15227   player->is_dropping = TRUE;
15228
15229   player->drop_pressed_delay = 0;
15230   player->is_dropping_pressed = FALSE;
15231
15232   player->drop_x = dropx;
15233   player->drop_y = dropy;
15234
15235   return TRUE;
15236 }
15237
15238 // ----------------------------------------------------------------------------
15239 // game sound playing functions
15240 // ----------------------------------------------------------------------------
15241
15242 static int *loop_sound_frame = NULL;
15243 static int *loop_sound_volume = NULL;
15244
15245 void InitPlayLevelSound(void)
15246 {
15247   int num_sounds = getSoundListSize();
15248
15249   checked_free(loop_sound_frame);
15250   checked_free(loop_sound_volume);
15251
15252   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
15253   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
15254 }
15255
15256 static void PlayLevelSound(int x, int y, int nr)
15257 {
15258   int sx = SCREENX(x), sy = SCREENY(y);
15259   int volume, stereo_position;
15260   int max_distance = 8;
15261   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
15262
15263   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
15264       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
15265     return;
15266
15267   if (!IN_LEV_FIELD(x, y) ||
15268       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
15269       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
15270     return;
15271
15272   volume = SOUND_MAX_VOLUME;
15273
15274   if (!IN_SCR_FIELD(sx, sy))
15275   {
15276     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
15277     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
15278
15279     volume -= volume * (dx > dy ? dx : dy) / max_distance;
15280   }
15281
15282   stereo_position = (SOUND_MAX_LEFT +
15283                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
15284                      (SCR_FIELDX + 2 * max_distance));
15285
15286   if (IS_LOOP_SOUND(nr))
15287   {
15288     /* This assures that quieter loop sounds do not overwrite louder ones,
15289        while restarting sound volume comparison with each new game frame. */
15290
15291     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
15292       return;
15293
15294     loop_sound_volume[nr] = volume;
15295     loop_sound_frame[nr] = FrameCounter;
15296   }
15297
15298   PlaySoundExt(nr, volume, stereo_position, type);
15299 }
15300
15301 static void PlayLevelSoundNearest(int x, int y, int sound_action)
15302 {
15303   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
15304                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
15305                  y < LEVELY(BY1) ? LEVELY(BY1) :
15306                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
15307                  sound_action);
15308 }
15309
15310 static void PlayLevelSoundAction(int x, int y, int action)
15311 {
15312   PlayLevelSoundElementAction(x, y, Tile[x][y], action);
15313 }
15314
15315 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
15316 {
15317   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15318
15319   if (sound_effect != SND_UNDEFINED)
15320     PlayLevelSound(x, y, sound_effect);
15321 }
15322
15323 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
15324                                               int action)
15325 {
15326   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15327
15328   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15329     PlayLevelSound(x, y, sound_effect);
15330 }
15331
15332 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
15333 {
15334   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15335
15336   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15337     PlayLevelSound(x, y, sound_effect);
15338 }
15339
15340 static void StopLevelSoundActionIfLoop(int x, int y, int action)
15341 {
15342   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15343
15344   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15345     StopSound(sound_effect);
15346 }
15347
15348 static int getLevelMusicNr(void)
15349 {
15350   int level_pos = level_nr - leveldir_current->first_level;
15351
15352   if (levelset.music[level_nr] != MUS_UNDEFINED)
15353     return levelset.music[level_nr];            // from config file
15354   else
15355     return MAP_NOCONF_MUSIC(level_pos);         // from music dir
15356 }
15357
15358 static void FadeLevelSounds(void)
15359 {
15360   FadeSounds();
15361 }
15362
15363 static void FadeLevelMusic(void)
15364 {
15365   int music_nr = getLevelMusicNr();
15366   char *curr_music = getCurrentlyPlayingMusicFilename();
15367   char *next_music = getMusicInfoEntryFilename(music_nr);
15368
15369   if (!strEqual(curr_music, next_music))
15370     FadeMusic();
15371 }
15372
15373 void FadeLevelSoundsAndMusic(void)
15374 {
15375   FadeLevelSounds();
15376   FadeLevelMusic();
15377 }
15378
15379 static void PlayLevelMusic(void)
15380 {
15381   int music_nr = getLevelMusicNr();
15382   char *curr_music = getCurrentlyPlayingMusicFilename();
15383   char *next_music = getMusicInfoEntryFilename(music_nr);
15384
15385   if (!strEqual(curr_music, next_music))
15386     PlayMusicLoop(music_nr);
15387 }
15388
15389 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15390 {
15391   int element = (element_em > -1 ? map_element_EM_to_RND_game(element_em) : 0);
15392   int offset = 0;
15393   int x = xx - offset;
15394   int y = yy - offset;
15395
15396   switch (sample)
15397   {
15398     case SOUND_blank:
15399       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15400       break;
15401
15402     case SOUND_roll:
15403       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15404       break;
15405
15406     case SOUND_stone:
15407       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15408       break;
15409
15410     case SOUND_nut:
15411       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15412       break;
15413
15414     case SOUND_crack:
15415       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15416       break;
15417
15418     case SOUND_bug:
15419       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15420       break;
15421
15422     case SOUND_tank:
15423       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15424       break;
15425
15426     case SOUND_android_clone:
15427       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15428       break;
15429
15430     case SOUND_android_move:
15431       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15432       break;
15433
15434     case SOUND_spring:
15435       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15436       break;
15437
15438     case SOUND_slurp:
15439       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15440       break;
15441
15442     case SOUND_eater:
15443       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15444       break;
15445
15446     case SOUND_eater_eat:
15447       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15448       break;
15449
15450     case SOUND_alien:
15451       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15452       break;
15453
15454     case SOUND_collect:
15455       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15456       break;
15457
15458     case SOUND_diamond:
15459       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15460       break;
15461
15462     case SOUND_squash:
15463       // !!! CHECK THIS !!!
15464 #if 1
15465       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15466 #else
15467       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15468 #endif
15469       break;
15470
15471     case SOUND_wonderfall:
15472       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15473       break;
15474
15475     case SOUND_drip:
15476       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15477       break;
15478
15479     case SOUND_push:
15480       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15481       break;
15482
15483     case SOUND_dirt:
15484       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15485       break;
15486
15487     case SOUND_acid:
15488       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15489       break;
15490
15491     case SOUND_ball:
15492       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15493       break;
15494
15495     case SOUND_slide:
15496       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15497       break;
15498
15499     case SOUND_wonder:
15500       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15501       break;
15502
15503     case SOUND_door:
15504       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15505       break;
15506
15507     case SOUND_exit_open:
15508       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15509       break;
15510
15511     case SOUND_exit_leave:
15512       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15513       break;
15514
15515     case SOUND_dynamite:
15516       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15517       break;
15518
15519     case SOUND_tick:
15520       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15521       break;
15522
15523     case SOUND_press:
15524       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15525       break;
15526
15527     case SOUND_wheel:
15528       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15529       break;
15530
15531     case SOUND_boom:
15532       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15533       break;
15534
15535     case SOUND_die:
15536       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15537       break;
15538
15539     case SOUND_time:
15540       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15541       break;
15542
15543     default:
15544       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15545       break;
15546   }
15547 }
15548
15549 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
15550 {
15551   int element = map_element_SP_to_RND(element_sp);
15552   int action = map_action_SP_to_RND(action_sp);
15553   int offset = (setup.sp_show_border_elements ? 0 : 1);
15554   int x = xx - offset;
15555   int y = yy - offset;
15556
15557   PlayLevelSoundElementAction(x, y, element, action);
15558 }
15559
15560 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
15561 {
15562   int element = map_element_MM_to_RND(element_mm);
15563   int action = map_action_MM_to_RND(action_mm);
15564   int offset = 0;
15565   int x = xx - offset;
15566   int y = yy - offset;
15567
15568   if (!IS_MM_ELEMENT(element))
15569     element = EL_MM_DEFAULT;
15570
15571   PlayLevelSoundElementAction(x, y, element, action);
15572 }
15573
15574 void PlaySound_MM(int sound_mm)
15575 {
15576   int sound = map_sound_MM_to_RND(sound_mm);
15577
15578   if (sound == SND_UNDEFINED)
15579     return;
15580
15581   PlaySound(sound);
15582 }
15583
15584 void PlaySoundLoop_MM(int sound_mm)
15585 {
15586   int sound = map_sound_MM_to_RND(sound_mm);
15587
15588   if (sound == SND_UNDEFINED)
15589     return;
15590
15591   PlaySoundLoop(sound);
15592 }
15593
15594 void StopSound_MM(int sound_mm)
15595 {
15596   int sound = map_sound_MM_to_RND(sound_mm);
15597
15598   if (sound == SND_UNDEFINED)
15599     return;
15600
15601   StopSound(sound);
15602 }
15603
15604 void RaiseScore(int value)
15605 {
15606   game.score += value;
15607
15608   game_panel_controls[GAME_PANEL_SCORE].value = game.score;
15609
15610   DisplayGameControlValues();
15611 }
15612
15613 void RaiseScoreElement(int element)
15614 {
15615   switch (element)
15616   {
15617     case EL_EMERALD:
15618     case EL_BD_DIAMOND:
15619     case EL_EMERALD_YELLOW:
15620     case EL_EMERALD_RED:
15621     case EL_EMERALD_PURPLE:
15622     case EL_SP_INFOTRON:
15623       RaiseScore(level.score[SC_EMERALD]);
15624       break;
15625     case EL_DIAMOND:
15626       RaiseScore(level.score[SC_DIAMOND]);
15627       break;
15628     case EL_CRYSTAL:
15629       RaiseScore(level.score[SC_CRYSTAL]);
15630       break;
15631     case EL_PEARL:
15632       RaiseScore(level.score[SC_PEARL]);
15633       break;
15634     case EL_BUG:
15635     case EL_BD_BUTTERFLY:
15636     case EL_SP_ELECTRON:
15637       RaiseScore(level.score[SC_BUG]);
15638       break;
15639     case EL_SPACESHIP:
15640     case EL_BD_FIREFLY:
15641     case EL_SP_SNIKSNAK:
15642       RaiseScore(level.score[SC_SPACESHIP]);
15643       break;
15644     case EL_YAMYAM:
15645     case EL_DARK_YAMYAM:
15646       RaiseScore(level.score[SC_YAMYAM]);
15647       break;
15648     case EL_ROBOT:
15649       RaiseScore(level.score[SC_ROBOT]);
15650       break;
15651     case EL_PACMAN:
15652       RaiseScore(level.score[SC_PACMAN]);
15653       break;
15654     case EL_NUT:
15655       RaiseScore(level.score[SC_NUT]);
15656       break;
15657     case EL_DYNAMITE:
15658     case EL_EM_DYNAMITE:
15659     case EL_SP_DISK_RED:
15660     case EL_DYNABOMB_INCREASE_NUMBER:
15661     case EL_DYNABOMB_INCREASE_SIZE:
15662     case EL_DYNABOMB_INCREASE_POWER:
15663       RaiseScore(level.score[SC_DYNAMITE]);
15664       break;
15665     case EL_SHIELD_NORMAL:
15666     case EL_SHIELD_DEADLY:
15667       RaiseScore(level.score[SC_SHIELD]);
15668       break;
15669     case EL_EXTRA_TIME:
15670       RaiseScore(level.extra_time_score);
15671       break;
15672     case EL_KEY_1:
15673     case EL_KEY_2:
15674     case EL_KEY_3:
15675     case EL_KEY_4:
15676     case EL_EM_KEY_1:
15677     case EL_EM_KEY_2:
15678     case EL_EM_KEY_3:
15679     case EL_EM_KEY_4:
15680     case EL_EMC_KEY_5:
15681     case EL_EMC_KEY_6:
15682     case EL_EMC_KEY_7:
15683     case EL_EMC_KEY_8:
15684     case EL_DC_KEY_WHITE:
15685       RaiseScore(level.score[SC_KEY]);
15686       break;
15687     default:
15688       RaiseScore(element_info[element].collect_score);
15689       break;
15690   }
15691 }
15692
15693 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15694 {
15695   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15696   {
15697     if (!quick_quit)
15698     {
15699       // prevent short reactivation of overlay buttons while closing door
15700       SetOverlayActive(FALSE);
15701       UnmapGameButtons();
15702
15703       // door may still be open due to skipped or envelope style request
15704       CloseDoor(score_info_tape_play ? DOOR_CLOSE_ALL : DOOR_CLOSE_1);
15705     }
15706
15707     if (network.enabled)
15708       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15709     else
15710     {
15711       if (quick_quit)
15712         FadeSkipNextFadeIn();
15713
15714       SetGameStatus(GAME_MODE_MAIN);
15715
15716       DrawMainMenu();
15717     }
15718   }
15719   else          // continue playing the game
15720   {
15721     if (tape.playing && tape.deactivate_display)
15722       TapeDeactivateDisplayOff(TRUE);
15723
15724     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15725
15726     if (tape.playing && tape.deactivate_display)
15727       TapeDeactivateDisplayOn();
15728   }
15729 }
15730
15731 void RequestQuitGame(boolean escape_key_pressed)
15732 {
15733   boolean ask_on_escape = (setup.ask_on_escape && setup.ask_on_quit_game);
15734   boolean quick_quit = ((escape_key_pressed && !ask_on_escape) ||
15735                         level_editor_test_game);
15736   boolean skip_request = (game.all_players_gone || !setup.ask_on_quit_game ||
15737                           quick_quit || score_info_tape_play);
15738
15739   RequestQuitGameExt(skip_request, quick_quit,
15740                      "Do you really want to quit the game?");
15741 }
15742
15743 static char *getRestartGameMessage(void)
15744 {
15745   boolean play_again = hasStartedNetworkGame();
15746   static char message[MAX_OUTPUT_LINESIZE];
15747   char *game_over_text = "Game over!";
15748   char *play_again_text = " Play it again?";
15749
15750   if (level.game_engine_type == GAME_ENGINE_TYPE_MM &&
15751       game_mm.game_over_message != NULL)
15752     game_over_text = game_mm.game_over_message;
15753
15754   snprintf(message, MAX_OUTPUT_LINESIZE, "%s%s", game_over_text,
15755            (play_again ? play_again_text : ""));
15756
15757   return message;
15758 }
15759
15760 static void RequestRestartGame(void)
15761 {
15762   char *message = getRestartGameMessage();
15763   boolean has_started_game = hasStartedNetworkGame();
15764   int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM);
15765   int door_state = DOOR_CLOSE_1;
15766
15767   if (Request(message, request_mode | REQ_STAY_OPEN) && has_started_game)
15768   {
15769     CloseDoor(door_state);
15770
15771     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15772   }
15773   else
15774   {
15775     // if game was invoked from level editor, also close tape recorder door
15776     if (level_editor_test_game)
15777       door_state = DOOR_CLOSE_ALL;
15778
15779     CloseDoor(door_state);
15780
15781     SetGameStatus(GAME_MODE_MAIN);
15782
15783     DrawMainMenu();
15784   }
15785 }
15786
15787 boolean CheckRestartGame(void)
15788 {
15789   static int game_over_delay = 0;
15790   int game_over_delay_value = 50;
15791   boolean game_over = checkGameFailed();
15792
15793   if (!game_over)
15794   {
15795     game_over_delay = game_over_delay_value;
15796
15797     return FALSE;
15798   }
15799
15800   if (game_over_delay > 0)
15801   {
15802     if (game_over_delay == game_over_delay_value / 2)
15803       PlaySound(SND_GAME_LOSING);
15804
15805     game_over_delay--;
15806
15807     return FALSE;
15808   }
15809
15810   // do not ask to play again if request dialog is already active
15811   if (game.request_active)
15812     return FALSE;
15813
15814   // do not ask to play again if request dialog already handled
15815   if (game.RestartGameRequested)
15816     return FALSE;
15817
15818   // do not ask to play again if game was never actually played
15819   if (!game.GamePlayed)
15820     return FALSE;
15821
15822   // do not ask to play again if this was disabled in setup menu
15823   if (!setup.ask_on_game_over)
15824     return FALSE;
15825
15826   game.RestartGameRequested = TRUE;
15827
15828   RequestRestartGame();
15829
15830   return TRUE;
15831 }
15832
15833 boolean checkGameSolved(void)
15834 {
15835   // set for all game engines if level was solved
15836   return game.LevelSolved_GameEnd;
15837 }
15838
15839 boolean checkGameFailed(void)
15840 {
15841   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15842     return (game_em.game_over && !game_em.level_solved);
15843   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15844     return (game_sp.game_over && !game_sp.level_solved);
15845   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15846     return (game_mm.game_over && !game_mm.level_solved);
15847   else                          // GAME_ENGINE_TYPE_RND
15848     return (game.GameOver && !game.LevelSolved);
15849 }
15850
15851 boolean checkGameEnded(void)
15852 {
15853   return (checkGameSolved() || checkGameFailed());
15854 }
15855
15856
15857 // ----------------------------------------------------------------------------
15858 // random generator functions
15859 // ----------------------------------------------------------------------------
15860
15861 unsigned int InitEngineRandom_RND(int seed)
15862 {
15863   game.num_random_calls = 0;
15864
15865   return InitEngineRandom(seed);
15866 }
15867
15868 unsigned int RND(int max)
15869 {
15870   if (max > 0)
15871   {
15872     game.num_random_calls++;
15873
15874     return GetEngineRandom(max);
15875   }
15876
15877   return 0;
15878 }
15879
15880
15881 // ----------------------------------------------------------------------------
15882 // game engine snapshot handling functions
15883 // ----------------------------------------------------------------------------
15884
15885 struct EngineSnapshotInfo
15886 {
15887   // runtime values for custom element collect score
15888   int collect_score[NUM_CUSTOM_ELEMENTS];
15889
15890   // runtime values for group element choice position
15891   int choice_pos[NUM_GROUP_ELEMENTS];
15892
15893   // runtime values for belt position animations
15894   int belt_graphic[4][NUM_BELT_PARTS];
15895   int belt_anim_mode[4][NUM_BELT_PARTS];
15896 };
15897
15898 static struct EngineSnapshotInfo engine_snapshot_rnd;
15899 static char *snapshot_level_identifier = NULL;
15900 static int snapshot_level_nr = -1;
15901
15902 static void SaveEngineSnapshotValues_RND(void)
15903 {
15904   static int belt_base_active_element[4] =
15905   {
15906     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15907     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15908     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15909     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15910   };
15911   int i, j;
15912
15913   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15914   {
15915     int element = EL_CUSTOM_START + i;
15916
15917     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15918   }
15919
15920   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15921   {
15922     int element = EL_GROUP_START + i;
15923
15924     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15925   }
15926
15927   for (i = 0; i < 4; i++)
15928   {
15929     for (j = 0; j < NUM_BELT_PARTS; j++)
15930     {
15931       int element = belt_base_active_element[i] + j;
15932       int graphic = el2img(element);
15933       int anim_mode = graphic_info[graphic].anim_mode;
15934
15935       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15936       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15937     }
15938   }
15939 }
15940
15941 static void LoadEngineSnapshotValues_RND(void)
15942 {
15943   unsigned int num_random_calls = game.num_random_calls;
15944   int i, j;
15945
15946   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15947   {
15948     int element = EL_CUSTOM_START + i;
15949
15950     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15951   }
15952
15953   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15954   {
15955     int element = EL_GROUP_START + i;
15956
15957     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15958   }
15959
15960   for (i = 0; i < 4; i++)
15961   {
15962     for (j = 0; j < NUM_BELT_PARTS; j++)
15963     {
15964       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15965       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15966
15967       graphic_info[graphic].anim_mode = anim_mode;
15968     }
15969   }
15970
15971   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15972   {
15973     InitRND(tape.random_seed);
15974     for (i = 0; i < num_random_calls; i++)
15975       RND(1);
15976   }
15977
15978   if (game.num_random_calls != num_random_calls)
15979   {
15980     Error("number of random calls out of sync");
15981     Error("number of random calls should be %d", num_random_calls);
15982     Error("number of random calls is %d", game.num_random_calls);
15983
15984     Fail("this should not happen -- please debug");
15985   }
15986 }
15987
15988 void FreeEngineSnapshotSingle(void)
15989 {
15990   FreeSnapshotSingle();
15991
15992   setString(&snapshot_level_identifier, NULL);
15993   snapshot_level_nr = -1;
15994 }
15995
15996 void FreeEngineSnapshotList(void)
15997 {
15998   FreeSnapshotList();
15999 }
16000
16001 static ListNode *SaveEngineSnapshotBuffers(void)
16002 {
16003   ListNode *buffers = NULL;
16004
16005   // copy some special values to a structure better suited for the snapshot
16006
16007   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16008     SaveEngineSnapshotValues_RND();
16009   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
16010     SaveEngineSnapshotValues_EM();
16011   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
16012     SaveEngineSnapshotValues_SP(&buffers);
16013   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
16014     SaveEngineSnapshotValues_MM();
16015
16016   // save values stored in special snapshot structure
16017
16018   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16019     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
16020   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
16021     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
16022   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
16023     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
16024   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
16025     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
16026
16027   // save further RND engine values
16028
16029   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
16030   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
16031   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
16032
16033   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
16034   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
16035   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
16036   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
16037   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
16038
16039   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
16040   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
16041   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
16042
16043   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
16044
16045   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
16046   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
16047
16048   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Tile));
16049   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
16050   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
16051   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
16052   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
16053   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
16054   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
16055   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
16056   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
16057   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
16058   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
16059   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
16060   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
16061   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
16062   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
16063   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
16064   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
16065   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
16066
16067   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
16068   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
16069
16070   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
16071   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
16072   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
16073
16074   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
16075   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
16076
16077   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
16078   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
16079   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandomStatic));
16080   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
16081   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
16082   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
16083
16084   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
16085   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
16086
16087 #if 0
16088   ListNode *node = engine_snapshot_list_rnd;
16089   int num_bytes = 0;
16090
16091   while (node != NULL)
16092   {
16093     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
16094
16095     node = node->next;
16096   }
16097
16098   Debug("game:playing:SaveEngineSnapshotBuffers",
16099         "size of engine snapshot: %d bytes", num_bytes);
16100 #endif
16101
16102   return buffers;
16103 }
16104
16105 void SaveEngineSnapshotSingle(void)
16106 {
16107   ListNode *buffers = SaveEngineSnapshotBuffers();
16108
16109   // finally save all snapshot buffers to single snapshot
16110   SaveSnapshotSingle(buffers);
16111
16112   // save level identification information
16113   setString(&snapshot_level_identifier, leveldir_current->identifier);
16114   snapshot_level_nr = level_nr;
16115 }
16116
16117 boolean CheckSaveEngineSnapshotToList(void)
16118 {
16119   boolean save_snapshot =
16120     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
16121      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
16122       game.snapshot.changed_action) ||
16123      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16124       game.snapshot.collected_item));
16125
16126   game.snapshot.changed_action = FALSE;
16127   game.snapshot.collected_item = FALSE;
16128   game.snapshot.save_snapshot = save_snapshot;
16129
16130   return save_snapshot;
16131 }
16132
16133 void SaveEngineSnapshotToList(void)
16134 {
16135   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
16136       tape.quick_resume)
16137     return;
16138
16139   ListNode *buffers = SaveEngineSnapshotBuffers();
16140
16141   // finally save all snapshot buffers to snapshot list
16142   SaveSnapshotToList(buffers);
16143 }
16144
16145 void SaveEngineSnapshotToListInitial(void)
16146 {
16147   FreeEngineSnapshotList();
16148
16149   SaveEngineSnapshotToList();
16150 }
16151
16152 static void LoadEngineSnapshotValues(void)
16153 {
16154   // restore special values from snapshot structure
16155
16156   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16157     LoadEngineSnapshotValues_RND();
16158   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
16159     LoadEngineSnapshotValues_EM();
16160   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
16161     LoadEngineSnapshotValues_SP();
16162   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
16163     LoadEngineSnapshotValues_MM();
16164 }
16165
16166 void LoadEngineSnapshotSingle(void)
16167 {
16168   LoadSnapshotSingle();
16169
16170   LoadEngineSnapshotValues();
16171 }
16172
16173 static void LoadEngineSnapshot_Undo(int steps)
16174 {
16175   LoadSnapshotFromList_Older(steps);
16176
16177   LoadEngineSnapshotValues();
16178 }
16179
16180 static void LoadEngineSnapshot_Redo(int steps)
16181 {
16182   LoadSnapshotFromList_Newer(steps);
16183
16184   LoadEngineSnapshotValues();
16185 }
16186
16187 boolean CheckEngineSnapshotSingle(void)
16188 {
16189   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16190           snapshot_level_nr == level_nr);
16191 }
16192
16193 boolean CheckEngineSnapshotList(void)
16194 {
16195   return CheckSnapshotList();
16196 }
16197
16198
16199 // ---------- new game button stuff -------------------------------------------
16200
16201 static struct
16202 {
16203   int graphic;
16204   struct XY *pos;
16205   int gadget_id;
16206   boolean *setup_value;
16207   boolean allowed_on_tape;
16208   boolean is_touch_button;
16209   char *infotext;
16210 } gamebutton_info[NUM_GAME_BUTTONS] =
16211 {
16212   {
16213     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
16214     GAME_CTRL_ID_STOP,                          NULL,
16215     TRUE, FALSE,                                "stop game"
16216   },
16217   {
16218     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
16219     GAME_CTRL_ID_PAUSE,                         NULL,
16220     TRUE, FALSE,                                "pause game"
16221   },
16222   {
16223     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
16224     GAME_CTRL_ID_PLAY,                          NULL,
16225     TRUE, FALSE,                                "play game"
16226   },
16227   {
16228     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
16229     GAME_CTRL_ID_UNDO,                          NULL,
16230     TRUE, FALSE,                                "undo step"
16231   },
16232   {
16233     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
16234     GAME_CTRL_ID_REDO,                          NULL,
16235     TRUE, FALSE,                                "redo step"
16236   },
16237   {
16238     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
16239     GAME_CTRL_ID_SAVE,                          NULL,
16240     TRUE, FALSE,                                "save game"
16241   },
16242   {
16243     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
16244     GAME_CTRL_ID_PAUSE2,                        NULL,
16245     TRUE, FALSE,                                "pause game"
16246   },
16247   {
16248     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
16249     GAME_CTRL_ID_LOAD,                          NULL,
16250     TRUE, FALSE,                                "load game"
16251   },
16252   {
16253     IMG_GFX_GAME_BUTTON_RESTART,                &game.button.restart,
16254     GAME_CTRL_ID_RESTART,                       NULL,
16255     TRUE, FALSE,                                "restart game"
16256   },
16257   {
16258     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
16259     GAME_CTRL_ID_PANEL_STOP,                    NULL,
16260     FALSE, FALSE,                               "stop game"
16261   },
16262   {
16263     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
16264     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
16265     FALSE, FALSE,                               "pause game"
16266   },
16267   {
16268     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
16269     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
16270     FALSE, FALSE,                               "play game"
16271   },
16272   {
16273     IMG_GFX_GAME_BUTTON_PANEL_RESTART,          &game.button.panel_restart,
16274     GAME_CTRL_ID_PANEL_RESTART,                 NULL,
16275     FALSE, FALSE,                               "restart game"
16276   },
16277   {
16278     IMG_GFX_GAME_BUTTON_TOUCH_STOP,             &game.button.touch_stop,
16279     GAME_CTRL_ID_TOUCH_STOP,                    NULL,
16280     FALSE, TRUE,                                "stop game"
16281   },
16282   {
16283     IMG_GFX_GAME_BUTTON_TOUCH_PAUSE,            &game.button.touch_pause,
16284     GAME_CTRL_ID_TOUCH_PAUSE,                   NULL,
16285     FALSE, TRUE,                                "pause game"
16286   },
16287   {
16288     IMG_GFX_GAME_BUTTON_TOUCH_RESTART,          &game.button.touch_restart,
16289     GAME_CTRL_ID_TOUCH_RESTART,                 NULL,
16290     FALSE, TRUE,                                "restart game"
16291   },
16292   {
16293     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
16294     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
16295     TRUE, FALSE,                                "background music on/off"
16296   },
16297   {
16298     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
16299     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
16300     TRUE, FALSE,                                "sound loops on/off"
16301   },
16302   {
16303     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
16304     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
16305     TRUE, FALSE,                                "normal sounds on/off"
16306   },
16307   {
16308     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
16309     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
16310     FALSE, FALSE,                               "background music on/off"
16311   },
16312   {
16313     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
16314     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
16315     FALSE, FALSE,                               "sound loops on/off"
16316   },
16317   {
16318     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
16319     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
16320     FALSE, FALSE,                               "normal sounds on/off"
16321   }
16322 };
16323
16324 void CreateGameButtons(void)
16325 {
16326   int i;
16327
16328   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16329   {
16330     int graphic = gamebutton_info[i].graphic;
16331     struct GraphicInfo *gfx = &graphic_info[graphic];
16332     struct XY *pos = gamebutton_info[i].pos;
16333     struct GadgetInfo *gi;
16334     int button_type;
16335     boolean checked;
16336     unsigned int event_mask;
16337     boolean is_touch_button = gamebutton_info[i].is_touch_button;
16338     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
16339     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
16340     int base_x = (is_touch_button ? 0 : on_tape ? VX : DX);
16341     int base_y = (is_touch_button ? 0 : on_tape ? VY : DY);
16342     int gd_x   = gfx->src_x;
16343     int gd_y   = gfx->src_y;
16344     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
16345     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
16346     int gd_xa  = gfx->src_x + gfx->active_xoffset;
16347     int gd_ya  = gfx->src_y + gfx->active_yoffset;
16348     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
16349     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
16350     int x = (is_touch_button ? pos->x : GDI_ACTIVE_POS(pos->x));
16351     int y = (is_touch_button ? pos->y : GDI_ACTIVE_POS(pos->y));
16352     int id = i;
16353
16354     // do not use touch buttons if overlay touch buttons are disabled
16355     if (is_touch_button && !setup.touch.overlay_buttons)
16356       continue;
16357
16358     if (gfx->bitmap == NULL)
16359     {
16360       game_gadget[id] = NULL;
16361
16362       continue;
16363     }
16364
16365     if (id == GAME_CTRL_ID_STOP ||
16366         id == GAME_CTRL_ID_PANEL_STOP ||
16367         id == GAME_CTRL_ID_TOUCH_STOP ||
16368         id == GAME_CTRL_ID_PLAY ||
16369         id == GAME_CTRL_ID_PANEL_PLAY ||
16370         id == GAME_CTRL_ID_SAVE ||
16371         id == GAME_CTRL_ID_LOAD ||
16372         id == GAME_CTRL_ID_RESTART ||
16373         id == GAME_CTRL_ID_PANEL_RESTART ||
16374         id == GAME_CTRL_ID_TOUCH_RESTART)
16375     {
16376       button_type = GD_TYPE_NORMAL_BUTTON;
16377       checked = FALSE;
16378       event_mask = GD_EVENT_RELEASED;
16379     }
16380     else if (id == GAME_CTRL_ID_UNDO ||
16381              id == GAME_CTRL_ID_REDO)
16382     {
16383       button_type = GD_TYPE_NORMAL_BUTTON;
16384       checked = FALSE;
16385       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
16386     }
16387     else
16388     {
16389       button_type = GD_TYPE_CHECK_BUTTON;
16390       checked = (gamebutton_info[i].setup_value != NULL ?
16391                  *gamebutton_info[i].setup_value : FALSE);
16392       event_mask = GD_EVENT_PRESSED;
16393     }
16394
16395     gi = CreateGadget(GDI_CUSTOM_ID, id,
16396                       GDI_IMAGE_ID, graphic,
16397                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
16398                       GDI_X, base_x + x,
16399                       GDI_Y, base_y + y,
16400                       GDI_WIDTH, gfx->width,
16401                       GDI_HEIGHT, gfx->height,
16402                       GDI_TYPE, button_type,
16403                       GDI_STATE, GD_BUTTON_UNPRESSED,
16404                       GDI_CHECKED, checked,
16405                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
16406                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
16407                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
16408                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
16409                       GDI_DIRECT_DRAW, FALSE,
16410                       GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
16411                       GDI_EVENT_MASK, event_mask,
16412                       GDI_CALLBACK_ACTION, HandleGameButtons,
16413                       GDI_END);
16414
16415     if (gi == NULL)
16416       Fail("cannot create gadget");
16417
16418     game_gadget[id] = gi;
16419   }
16420 }
16421
16422 void FreeGameButtons(void)
16423 {
16424   int i;
16425
16426   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16427     FreeGadget(game_gadget[i]);
16428 }
16429
16430 static void UnmapGameButtonsAtSamePosition(int id)
16431 {
16432   int i;
16433
16434   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16435     if (i != id &&
16436         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
16437         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
16438       UnmapGadget(game_gadget[i]);
16439 }
16440
16441 static void UnmapGameButtonsAtSamePosition_All(void)
16442 {
16443   if (setup.show_load_save_buttons)
16444   {
16445     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16446     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16447     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16448   }
16449   else if (setup.show_undo_redo_buttons)
16450   {
16451     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16452     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16453     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16454   }
16455   else
16456   {
16457     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
16458     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
16459     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
16460
16461     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
16462     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
16463     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
16464   }
16465 }
16466
16467 void MapLoadSaveButtons(void)
16468 {
16469   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16470   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16471
16472   MapGadget(game_gadget[GAME_CTRL_ID_LOAD]);
16473   MapGadget(game_gadget[GAME_CTRL_ID_SAVE]);
16474 }
16475
16476 void MapUndoRedoButtons(void)
16477 {
16478   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16479   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16480
16481   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
16482   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
16483 }
16484
16485 void ModifyPauseButtons(void)
16486 {
16487   static int ids[] =
16488   {
16489     GAME_CTRL_ID_PAUSE,
16490     GAME_CTRL_ID_PAUSE2,
16491     GAME_CTRL_ID_PANEL_PAUSE,
16492     GAME_CTRL_ID_TOUCH_PAUSE,
16493     -1
16494   };
16495   int i;
16496
16497   // do not redraw pause button on closed door (may happen when restarting game)
16498   if (!(GetDoorState() & DOOR_OPEN_1))
16499     return;
16500
16501   for (i = 0; ids[i] > -1; i++)
16502     ModifyGadget(game_gadget[ids[i]], GDI_CHECKED, tape.pausing, GDI_END);
16503 }
16504
16505 static void MapGameButtonsExt(boolean on_tape)
16506 {
16507   int i;
16508
16509   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16510   {
16511     if ((i == GAME_CTRL_ID_UNDO ||
16512          i == GAME_CTRL_ID_REDO) &&
16513         game_status != GAME_MODE_PLAYING)
16514       continue;
16515
16516     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16517       MapGadget(game_gadget[i]);
16518   }
16519
16520   UnmapGameButtonsAtSamePosition_All();
16521
16522   RedrawGameButtons();
16523 }
16524
16525 static void UnmapGameButtonsExt(boolean on_tape)
16526 {
16527   int i;
16528
16529   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16530     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16531       UnmapGadget(game_gadget[i]);
16532 }
16533
16534 static void RedrawGameButtonsExt(boolean on_tape)
16535 {
16536   int i;
16537
16538   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16539     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16540       RedrawGadget(game_gadget[i]);
16541 }
16542
16543 static void SetGadgetState(struct GadgetInfo *gi, boolean state)
16544 {
16545   if (gi == NULL)
16546     return;
16547
16548   gi->checked = state;
16549 }
16550
16551 static void RedrawSoundButtonGadget(int id)
16552 {
16553   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
16554              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
16555              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
16556              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
16557              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
16558              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
16559              id);
16560
16561   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
16562   RedrawGadget(game_gadget[id2]);
16563 }
16564
16565 void MapGameButtons(void)
16566 {
16567   MapGameButtonsExt(FALSE);
16568 }
16569
16570 void UnmapGameButtons(void)
16571 {
16572   UnmapGameButtonsExt(FALSE);
16573 }
16574
16575 void RedrawGameButtons(void)
16576 {
16577   RedrawGameButtonsExt(FALSE);
16578 }
16579
16580 void MapGameButtonsOnTape(void)
16581 {
16582   MapGameButtonsExt(TRUE);
16583 }
16584
16585 void UnmapGameButtonsOnTape(void)
16586 {
16587   UnmapGameButtonsExt(TRUE);
16588 }
16589
16590 void RedrawGameButtonsOnTape(void)
16591 {
16592   RedrawGameButtonsExt(TRUE);
16593 }
16594
16595 static void GameUndoRedoExt(void)
16596 {
16597   ClearPlayerAction();
16598
16599   tape.pausing = TRUE;
16600
16601   RedrawPlayfield();
16602   UpdateAndDisplayGameControlValues();
16603
16604   DrawCompleteVideoDisplay();
16605   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
16606   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
16607   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
16608
16609   ModifyPauseButtons();
16610
16611   BackToFront();
16612 }
16613
16614 static void GameUndo(int steps)
16615 {
16616   if (!CheckEngineSnapshotList())
16617     return;
16618
16619   int tape_property_bits = tape.property_bits;
16620
16621   LoadEngineSnapshot_Undo(steps);
16622
16623   tape.property_bits |= tape_property_bits | TAPE_PROPERTY_SNAPSHOT;
16624
16625   GameUndoRedoExt();
16626 }
16627
16628 static void GameRedo(int steps)
16629 {
16630   if (!CheckEngineSnapshotList())
16631     return;
16632
16633   int tape_property_bits = tape.property_bits;
16634
16635   LoadEngineSnapshot_Redo(steps);
16636
16637   tape.property_bits |= tape_property_bits | TAPE_PROPERTY_SNAPSHOT;
16638
16639   GameUndoRedoExt();
16640 }
16641
16642 static void HandleGameButtonsExt(int id, int button)
16643 {
16644   static boolean game_undo_executed = FALSE;
16645   int steps = BUTTON_STEPSIZE(button);
16646   boolean handle_game_buttons =
16647     (game_status == GAME_MODE_PLAYING ||
16648      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
16649
16650   if (!handle_game_buttons)
16651     return;
16652
16653   switch (id)
16654   {
16655     case GAME_CTRL_ID_STOP:
16656     case GAME_CTRL_ID_PANEL_STOP:
16657     case GAME_CTRL_ID_TOUCH_STOP:
16658       TapeStopGame();
16659
16660       break;
16661
16662     case GAME_CTRL_ID_PAUSE:
16663     case GAME_CTRL_ID_PAUSE2:
16664     case GAME_CTRL_ID_PANEL_PAUSE:
16665     case GAME_CTRL_ID_TOUCH_PAUSE:
16666       if (network.enabled && game_status == GAME_MODE_PLAYING)
16667       {
16668         if (tape.pausing)
16669           SendToServer_ContinuePlaying();
16670         else
16671           SendToServer_PausePlaying();
16672       }
16673       else
16674         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16675
16676       game_undo_executed = FALSE;
16677
16678       break;
16679
16680     case GAME_CTRL_ID_PLAY:
16681     case GAME_CTRL_ID_PANEL_PLAY:
16682       if (game_status == GAME_MODE_MAIN)
16683       {
16684         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
16685       }
16686       else if (tape.pausing)
16687       {
16688         if (network.enabled)
16689           SendToServer_ContinuePlaying();
16690         else
16691           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
16692       }
16693       break;
16694
16695     case GAME_CTRL_ID_UNDO:
16696       // Important: When using "save snapshot when collecting an item" mode,
16697       // load last (current) snapshot for first "undo" after pressing "pause"
16698       // (else the last-but-one snapshot would be loaded, because the snapshot
16699       // pointer already points to the last snapshot when pressing "pause",
16700       // which is fine for "every step/move" mode, but not for "every collect")
16701       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16702           !game_undo_executed)
16703         steps--;
16704
16705       game_undo_executed = TRUE;
16706
16707       GameUndo(steps);
16708       break;
16709
16710     case GAME_CTRL_ID_REDO:
16711       GameRedo(steps);
16712       break;
16713
16714     case GAME_CTRL_ID_SAVE:
16715       TapeQuickSave();
16716       break;
16717
16718     case GAME_CTRL_ID_LOAD:
16719       TapeQuickLoad();
16720       break;
16721
16722     case GAME_CTRL_ID_RESTART:
16723     case GAME_CTRL_ID_PANEL_RESTART:
16724     case GAME_CTRL_ID_TOUCH_RESTART:
16725       TapeRestartGame();
16726
16727       break;
16728
16729     case SOUND_CTRL_ID_MUSIC:
16730     case SOUND_CTRL_ID_PANEL_MUSIC:
16731       if (setup.sound_music)
16732       { 
16733         setup.sound_music = FALSE;
16734
16735         FadeMusic();
16736       }
16737       else if (audio.music_available)
16738       { 
16739         setup.sound = setup.sound_music = TRUE;
16740
16741         SetAudioMode(setup.sound);
16742
16743         if (game_status == GAME_MODE_PLAYING)
16744           PlayLevelMusic();
16745       }
16746
16747       RedrawSoundButtonGadget(id);
16748
16749       break;
16750
16751     case SOUND_CTRL_ID_LOOPS:
16752     case SOUND_CTRL_ID_PANEL_LOOPS:
16753       if (setup.sound_loops)
16754         setup.sound_loops = FALSE;
16755       else if (audio.loops_available)
16756       {
16757         setup.sound = setup.sound_loops = TRUE;
16758
16759         SetAudioMode(setup.sound);
16760       }
16761
16762       RedrawSoundButtonGadget(id);
16763
16764       break;
16765
16766     case SOUND_CTRL_ID_SIMPLE:
16767     case SOUND_CTRL_ID_PANEL_SIMPLE:
16768       if (setup.sound_simple)
16769         setup.sound_simple = FALSE;
16770       else if (audio.sound_available)
16771       {
16772         setup.sound = setup.sound_simple = TRUE;
16773
16774         SetAudioMode(setup.sound);
16775       }
16776
16777       RedrawSoundButtonGadget(id);
16778
16779       break;
16780
16781     default:
16782       break;
16783   }
16784 }
16785
16786 static void HandleGameButtons(struct GadgetInfo *gi)
16787 {
16788   HandleGameButtonsExt(gi->custom_id, gi->event.button);
16789 }
16790
16791 void HandleSoundButtonKeys(Key key)
16792 {
16793   if (key == setup.shortcut.sound_simple)
16794     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
16795   else if (key == setup.shortcut.sound_loops)
16796     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
16797   else if (key == setup.shortcut.sound_music)
16798     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
16799 }