removed duplicate variable to check if request dialog is active
[rocksndiamonds.git] / src / game.c
1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // game.c
10 // ============================================================================
11
12 #include "libgame/libgame.h"
13
14 #include "game.h"
15 #include "init.h"
16 #include "tools.h"
17 #include "screens.h"
18 #include "events.h"
19 #include "files.h"
20 #include "tape.h"
21 #include "network.h"
22 #include "anim.h"
23
24
25 // DEBUG SETTINGS
26 #define DEBUG_INIT_PLAYER       1
27 #define DEBUG_PLAYER_ACTIONS    0
28
29 // EXPERIMENTAL STUFF
30 #define USE_NEW_AMOEBA_CODE     FALSE
31
32 // EXPERIMENTAL STUFF
33 #define USE_QUICKSAND_BD_ROCK_BUGFIX    0
34 #define USE_QUICKSAND_IMPACT_BUGFIX     0
35 #define USE_DELAYED_GFX_REDRAW          0
36 #define USE_NEW_PLAYER_ASSIGNMENTS      1
37
38 #if USE_DELAYED_GFX_REDRAW
39 #define TEST_DrawLevelField(x, y)                               \
40         GfxRedraw[x][y] |= GFX_REDRAW_TILE
41 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
42         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
43 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
44         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
45 #define TEST_DrawTwinkleOnField(x, y)                           \
46         GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
47 #else
48 #define TEST_DrawLevelField(x, y)                               \
49              DrawLevelField(x, y)
50 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
51              DrawLevelFieldCrumbled(x, y)
52 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
53              DrawLevelFieldCrumbledNeighbours(x, y)
54 #define TEST_DrawTwinkleOnField(x, y)                           \
55              DrawTwinkleOnField(x, y)
56 #endif
57
58
59 // for DigField()
60 #define DF_NO_PUSH              0
61 #define DF_DIG                  1
62 #define DF_SNAP                 2
63
64 // for MovePlayer()
65 #define MP_NO_ACTION            0
66 #define MP_MOVING               1
67 #define MP_ACTION               2
68 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
69
70 // for ScrollPlayer()
71 #define SCROLL_INIT             0
72 #define SCROLL_GO_ON            1
73
74 // for Bang()/Explode()
75 #define EX_PHASE_START          0
76 #define EX_TYPE_NONE            0
77 #define EX_TYPE_NORMAL          (1 << 0)
78 #define EX_TYPE_CENTER          (1 << 1)
79 #define EX_TYPE_BORDER          (1 << 2)
80 #define EX_TYPE_CROSS           (1 << 3)
81 #define EX_TYPE_DYNA            (1 << 4)
82 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
83
84 #define PANEL_OFF()             (game.panel.active == FALSE)
85 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
86 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
87 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
88
89 // game panel display and control definitions
90 #define GAME_PANEL_LEVEL_NUMBER                 0
91 #define GAME_PANEL_GEMS                         1
92 #define GAME_PANEL_INVENTORY_COUNT              2
93 #define GAME_PANEL_INVENTORY_FIRST_1            3
94 #define GAME_PANEL_INVENTORY_FIRST_2            4
95 #define GAME_PANEL_INVENTORY_FIRST_3            5
96 #define GAME_PANEL_INVENTORY_FIRST_4            6
97 #define GAME_PANEL_INVENTORY_FIRST_5            7
98 #define GAME_PANEL_INVENTORY_FIRST_6            8
99 #define GAME_PANEL_INVENTORY_FIRST_7            9
100 #define GAME_PANEL_INVENTORY_FIRST_8            10
101 #define GAME_PANEL_INVENTORY_LAST_1             11
102 #define GAME_PANEL_INVENTORY_LAST_2             12
103 #define GAME_PANEL_INVENTORY_LAST_3             13
104 #define GAME_PANEL_INVENTORY_LAST_4             14
105 #define GAME_PANEL_INVENTORY_LAST_5             15
106 #define GAME_PANEL_INVENTORY_LAST_6             16
107 #define GAME_PANEL_INVENTORY_LAST_7             17
108 #define GAME_PANEL_INVENTORY_LAST_8             18
109 #define GAME_PANEL_KEY_1                        19
110 #define GAME_PANEL_KEY_2                        20
111 #define GAME_PANEL_KEY_3                        21
112 #define GAME_PANEL_KEY_4                        22
113 #define GAME_PANEL_KEY_5                        23
114 #define GAME_PANEL_KEY_6                        24
115 #define GAME_PANEL_KEY_7                        25
116 #define GAME_PANEL_KEY_8                        26
117 #define GAME_PANEL_KEY_WHITE                    27
118 #define GAME_PANEL_KEY_WHITE_COUNT              28
119 #define GAME_PANEL_SCORE                        29
120 #define GAME_PANEL_HIGHSCORE                    30
121 #define GAME_PANEL_TIME                         31
122 #define GAME_PANEL_TIME_HH                      32
123 #define GAME_PANEL_TIME_MM                      33
124 #define GAME_PANEL_TIME_SS                      34
125 #define GAME_PANEL_TIME_ANIM                    35
126 #define GAME_PANEL_HEALTH                       36
127 #define GAME_PANEL_HEALTH_ANIM                  37
128 #define GAME_PANEL_FRAME                        38
129 #define GAME_PANEL_SHIELD_NORMAL                39
130 #define GAME_PANEL_SHIELD_NORMAL_TIME           40
131 #define GAME_PANEL_SHIELD_DEADLY                41
132 #define GAME_PANEL_SHIELD_DEADLY_TIME           42
133 #define GAME_PANEL_EXIT                         43
134 #define GAME_PANEL_EMC_MAGIC_BALL               44
135 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        45
136 #define GAME_PANEL_LIGHT_SWITCH                 46
137 #define GAME_PANEL_LIGHT_SWITCH_TIME            47
138 #define GAME_PANEL_TIMEGATE_SWITCH              48
139 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         49
140 #define GAME_PANEL_SWITCHGATE_SWITCH            50
141 #define GAME_PANEL_EMC_LENSES                   51
142 #define GAME_PANEL_EMC_LENSES_TIME              52
143 #define GAME_PANEL_EMC_MAGNIFIER                53
144 #define GAME_PANEL_EMC_MAGNIFIER_TIME           54
145 #define GAME_PANEL_BALLOON_SWITCH               55
146 #define GAME_PANEL_DYNABOMB_NUMBER              56
147 #define GAME_PANEL_DYNABOMB_SIZE                57
148 #define GAME_PANEL_DYNABOMB_POWER               58
149 #define GAME_PANEL_PENGUINS                     59
150 #define GAME_PANEL_SOKOBAN_OBJECTS              60
151 #define GAME_PANEL_SOKOBAN_FIELDS               61
152 #define GAME_PANEL_ROBOT_WHEEL                  62
153 #define GAME_PANEL_CONVEYOR_BELT_1              63
154 #define GAME_PANEL_CONVEYOR_BELT_2              64
155 #define GAME_PANEL_CONVEYOR_BELT_3              65
156 #define GAME_PANEL_CONVEYOR_BELT_4              66
157 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       67
158 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       68
159 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       69
160 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       70
161 #define GAME_PANEL_MAGIC_WALL                   71
162 #define GAME_PANEL_MAGIC_WALL_TIME              72
163 #define GAME_PANEL_GRAVITY_STATE                73
164 #define GAME_PANEL_GRAPHIC_1                    74
165 #define GAME_PANEL_GRAPHIC_2                    75
166 #define GAME_PANEL_GRAPHIC_3                    76
167 #define GAME_PANEL_GRAPHIC_4                    77
168 #define GAME_PANEL_GRAPHIC_5                    78
169 #define GAME_PANEL_GRAPHIC_6                    79
170 #define GAME_PANEL_GRAPHIC_7                    80
171 #define GAME_PANEL_GRAPHIC_8                    81
172 #define GAME_PANEL_ELEMENT_1                    82
173 #define GAME_PANEL_ELEMENT_2                    83
174 #define GAME_PANEL_ELEMENT_3                    84
175 #define GAME_PANEL_ELEMENT_4                    85
176 #define GAME_PANEL_ELEMENT_5                    86
177 #define GAME_PANEL_ELEMENT_6                    87
178 #define GAME_PANEL_ELEMENT_7                    88
179 #define GAME_PANEL_ELEMENT_8                    89
180 #define GAME_PANEL_ELEMENT_COUNT_1              90
181 #define GAME_PANEL_ELEMENT_COUNT_2              91
182 #define GAME_PANEL_ELEMENT_COUNT_3              92
183 #define GAME_PANEL_ELEMENT_COUNT_4              93
184 #define GAME_PANEL_ELEMENT_COUNT_5              94
185 #define GAME_PANEL_ELEMENT_COUNT_6              95
186 #define GAME_PANEL_ELEMENT_COUNT_7              96
187 #define GAME_PANEL_ELEMENT_COUNT_8              97
188 #define GAME_PANEL_CE_SCORE_1                   98
189 #define GAME_PANEL_CE_SCORE_2                   99
190 #define GAME_PANEL_CE_SCORE_3                   100
191 #define GAME_PANEL_CE_SCORE_4                   101
192 #define GAME_PANEL_CE_SCORE_5                   102
193 #define GAME_PANEL_CE_SCORE_6                   103
194 #define GAME_PANEL_CE_SCORE_7                   104
195 #define GAME_PANEL_CE_SCORE_8                   105
196 #define GAME_PANEL_CE_SCORE_1_ELEMENT           106
197 #define GAME_PANEL_CE_SCORE_2_ELEMENT           107
198 #define GAME_PANEL_CE_SCORE_3_ELEMENT           108
199 #define GAME_PANEL_CE_SCORE_4_ELEMENT           109
200 #define GAME_PANEL_CE_SCORE_5_ELEMENT           110
201 #define GAME_PANEL_CE_SCORE_6_ELEMENT           111
202 #define GAME_PANEL_CE_SCORE_7_ELEMENT           112
203 #define GAME_PANEL_CE_SCORE_8_ELEMENT           113
204 #define GAME_PANEL_PLAYER_NAME                  114
205 #define GAME_PANEL_LEVEL_NAME                   115
206 #define GAME_PANEL_LEVEL_AUTHOR                 116
207
208 #define NUM_GAME_PANEL_CONTROLS                 117
209
210 struct GamePanelOrderInfo
211 {
212   int nr;
213   int sort_priority;
214 };
215
216 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
217
218 struct GamePanelControlInfo
219 {
220   int nr;
221
222   struct TextPosInfo *pos;
223   int type;
224
225   int graphic, graphic_active;
226
227   int value, last_value;
228   int frame, last_frame;
229   int gfx_frame;
230   int gfx_random;
231 };
232
233 static struct GamePanelControlInfo game_panel_controls[] =
234 {
235   {
236     GAME_PANEL_LEVEL_NUMBER,
237     &game.panel.level_number,
238     TYPE_INTEGER,
239   },
240   {
241     GAME_PANEL_GEMS,
242     &game.panel.gems,
243     TYPE_INTEGER,
244   },
245   {
246     GAME_PANEL_INVENTORY_COUNT,
247     &game.panel.inventory_count,
248     TYPE_INTEGER,
249   },
250   {
251     GAME_PANEL_INVENTORY_FIRST_1,
252     &game.panel.inventory_first[0],
253     TYPE_ELEMENT,
254   },
255   {
256     GAME_PANEL_INVENTORY_FIRST_2,
257     &game.panel.inventory_first[1],
258     TYPE_ELEMENT,
259   },
260   {
261     GAME_PANEL_INVENTORY_FIRST_3,
262     &game.panel.inventory_first[2],
263     TYPE_ELEMENT,
264   },
265   {
266     GAME_PANEL_INVENTORY_FIRST_4,
267     &game.panel.inventory_first[3],
268     TYPE_ELEMENT,
269   },
270   {
271     GAME_PANEL_INVENTORY_FIRST_5,
272     &game.panel.inventory_first[4],
273     TYPE_ELEMENT,
274   },
275   {
276     GAME_PANEL_INVENTORY_FIRST_6,
277     &game.panel.inventory_first[5],
278     TYPE_ELEMENT,
279   },
280   {
281     GAME_PANEL_INVENTORY_FIRST_7,
282     &game.panel.inventory_first[6],
283     TYPE_ELEMENT,
284   },
285   {
286     GAME_PANEL_INVENTORY_FIRST_8,
287     &game.panel.inventory_first[7],
288     TYPE_ELEMENT,
289   },
290   {
291     GAME_PANEL_INVENTORY_LAST_1,
292     &game.panel.inventory_last[0],
293     TYPE_ELEMENT,
294   },
295   {
296     GAME_PANEL_INVENTORY_LAST_2,
297     &game.panel.inventory_last[1],
298     TYPE_ELEMENT,
299   },
300   {
301     GAME_PANEL_INVENTORY_LAST_3,
302     &game.panel.inventory_last[2],
303     TYPE_ELEMENT,
304   },
305   {
306     GAME_PANEL_INVENTORY_LAST_4,
307     &game.panel.inventory_last[3],
308     TYPE_ELEMENT,
309   },
310   {
311     GAME_PANEL_INVENTORY_LAST_5,
312     &game.panel.inventory_last[4],
313     TYPE_ELEMENT,
314   },
315   {
316     GAME_PANEL_INVENTORY_LAST_6,
317     &game.panel.inventory_last[5],
318     TYPE_ELEMENT,
319   },
320   {
321     GAME_PANEL_INVENTORY_LAST_7,
322     &game.panel.inventory_last[6],
323     TYPE_ELEMENT,
324   },
325   {
326     GAME_PANEL_INVENTORY_LAST_8,
327     &game.panel.inventory_last[7],
328     TYPE_ELEMENT,
329   },
330   {
331     GAME_PANEL_KEY_1,
332     &game.panel.key[0],
333     TYPE_ELEMENT,
334   },
335   {
336     GAME_PANEL_KEY_2,
337     &game.panel.key[1],
338     TYPE_ELEMENT,
339   },
340   {
341     GAME_PANEL_KEY_3,
342     &game.panel.key[2],
343     TYPE_ELEMENT,
344   },
345   {
346     GAME_PANEL_KEY_4,
347     &game.panel.key[3],
348     TYPE_ELEMENT,
349   },
350   {
351     GAME_PANEL_KEY_5,
352     &game.panel.key[4],
353     TYPE_ELEMENT,
354   },
355   {
356     GAME_PANEL_KEY_6,
357     &game.panel.key[5],
358     TYPE_ELEMENT,
359   },
360   {
361     GAME_PANEL_KEY_7,
362     &game.panel.key[6],
363     TYPE_ELEMENT,
364   },
365   {
366     GAME_PANEL_KEY_8,
367     &game.panel.key[7],
368     TYPE_ELEMENT,
369   },
370   {
371     GAME_PANEL_KEY_WHITE,
372     &game.panel.key_white,
373     TYPE_ELEMENT,
374   },
375   {
376     GAME_PANEL_KEY_WHITE_COUNT,
377     &game.panel.key_white_count,
378     TYPE_INTEGER,
379   },
380   {
381     GAME_PANEL_SCORE,
382     &game.panel.score,
383     TYPE_INTEGER,
384   },
385   {
386     GAME_PANEL_HIGHSCORE,
387     &game.panel.highscore,
388     TYPE_INTEGER,
389   },
390   {
391     GAME_PANEL_TIME,
392     &game.panel.time,
393     TYPE_INTEGER,
394   },
395   {
396     GAME_PANEL_TIME_HH,
397     &game.panel.time_hh,
398     TYPE_INTEGER,
399   },
400   {
401     GAME_PANEL_TIME_MM,
402     &game.panel.time_mm,
403     TYPE_INTEGER,
404   },
405   {
406     GAME_PANEL_TIME_SS,
407     &game.panel.time_ss,
408     TYPE_INTEGER,
409   },
410   {
411     GAME_PANEL_TIME_ANIM,
412     &game.panel.time_anim,
413     TYPE_GRAPHIC,
414
415     IMG_GFX_GAME_PANEL_TIME_ANIM,
416     IMG_GFX_GAME_PANEL_TIME_ANIM_ACTIVE
417   },
418   {
419     GAME_PANEL_HEALTH,
420     &game.panel.health,
421     TYPE_INTEGER,
422   },
423   {
424     GAME_PANEL_HEALTH_ANIM,
425     &game.panel.health_anim,
426     TYPE_GRAPHIC,
427
428     IMG_GFX_GAME_PANEL_HEALTH_ANIM,
429     IMG_GFX_GAME_PANEL_HEALTH_ANIM_ACTIVE
430   },
431   {
432     GAME_PANEL_FRAME,
433     &game.panel.frame,
434     TYPE_INTEGER,
435   },
436   {
437     GAME_PANEL_SHIELD_NORMAL,
438     &game.panel.shield_normal,
439     TYPE_ELEMENT,
440   },
441   {
442     GAME_PANEL_SHIELD_NORMAL_TIME,
443     &game.panel.shield_normal_time,
444     TYPE_INTEGER,
445   },
446   {
447     GAME_PANEL_SHIELD_DEADLY,
448     &game.panel.shield_deadly,
449     TYPE_ELEMENT,
450   },
451   {
452     GAME_PANEL_SHIELD_DEADLY_TIME,
453     &game.panel.shield_deadly_time,
454     TYPE_INTEGER,
455   },
456   {
457     GAME_PANEL_EXIT,
458     &game.panel.exit,
459     TYPE_ELEMENT,
460   },
461   {
462     GAME_PANEL_EMC_MAGIC_BALL,
463     &game.panel.emc_magic_ball,
464     TYPE_ELEMENT,
465   },
466   {
467     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
468     &game.panel.emc_magic_ball_switch,
469     TYPE_ELEMENT,
470   },
471   {
472     GAME_PANEL_LIGHT_SWITCH,
473     &game.panel.light_switch,
474     TYPE_ELEMENT,
475   },
476   {
477     GAME_PANEL_LIGHT_SWITCH_TIME,
478     &game.panel.light_switch_time,
479     TYPE_INTEGER,
480   },
481   {
482     GAME_PANEL_TIMEGATE_SWITCH,
483     &game.panel.timegate_switch,
484     TYPE_ELEMENT,
485   },
486   {
487     GAME_PANEL_TIMEGATE_SWITCH_TIME,
488     &game.panel.timegate_switch_time,
489     TYPE_INTEGER,
490   },
491   {
492     GAME_PANEL_SWITCHGATE_SWITCH,
493     &game.panel.switchgate_switch,
494     TYPE_ELEMENT,
495   },
496   {
497     GAME_PANEL_EMC_LENSES,
498     &game.panel.emc_lenses,
499     TYPE_ELEMENT,
500   },
501   {
502     GAME_PANEL_EMC_LENSES_TIME,
503     &game.panel.emc_lenses_time,
504     TYPE_INTEGER,
505   },
506   {
507     GAME_PANEL_EMC_MAGNIFIER,
508     &game.panel.emc_magnifier,
509     TYPE_ELEMENT,
510   },
511   {
512     GAME_PANEL_EMC_MAGNIFIER_TIME,
513     &game.panel.emc_magnifier_time,
514     TYPE_INTEGER,
515   },
516   {
517     GAME_PANEL_BALLOON_SWITCH,
518     &game.panel.balloon_switch,
519     TYPE_ELEMENT,
520   },
521   {
522     GAME_PANEL_DYNABOMB_NUMBER,
523     &game.panel.dynabomb_number,
524     TYPE_INTEGER,
525   },
526   {
527     GAME_PANEL_DYNABOMB_SIZE,
528     &game.panel.dynabomb_size,
529     TYPE_INTEGER,
530   },
531   {
532     GAME_PANEL_DYNABOMB_POWER,
533     &game.panel.dynabomb_power,
534     TYPE_ELEMENT,
535   },
536   {
537     GAME_PANEL_PENGUINS,
538     &game.panel.penguins,
539     TYPE_INTEGER,
540   },
541   {
542     GAME_PANEL_SOKOBAN_OBJECTS,
543     &game.panel.sokoban_objects,
544     TYPE_INTEGER,
545   },
546   {
547     GAME_PANEL_SOKOBAN_FIELDS,
548     &game.panel.sokoban_fields,
549     TYPE_INTEGER,
550   },
551   {
552     GAME_PANEL_ROBOT_WHEEL,
553     &game.panel.robot_wheel,
554     TYPE_ELEMENT,
555   },
556   {
557     GAME_PANEL_CONVEYOR_BELT_1,
558     &game.panel.conveyor_belt[0],
559     TYPE_ELEMENT,
560   },
561   {
562     GAME_PANEL_CONVEYOR_BELT_2,
563     &game.panel.conveyor_belt[1],
564     TYPE_ELEMENT,
565   },
566   {
567     GAME_PANEL_CONVEYOR_BELT_3,
568     &game.panel.conveyor_belt[2],
569     TYPE_ELEMENT,
570   },
571   {
572     GAME_PANEL_CONVEYOR_BELT_4,
573     &game.panel.conveyor_belt[3],
574     TYPE_ELEMENT,
575   },
576   {
577     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
578     &game.panel.conveyor_belt_switch[0],
579     TYPE_ELEMENT,
580   },
581   {
582     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
583     &game.panel.conveyor_belt_switch[1],
584     TYPE_ELEMENT,
585   },
586   {
587     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
588     &game.panel.conveyor_belt_switch[2],
589     TYPE_ELEMENT,
590   },
591   {
592     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
593     &game.panel.conveyor_belt_switch[3],
594     TYPE_ELEMENT,
595   },
596   {
597     GAME_PANEL_MAGIC_WALL,
598     &game.panel.magic_wall,
599     TYPE_ELEMENT,
600   },
601   {
602     GAME_PANEL_MAGIC_WALL_TIME,
603     &game.panel.magic_wall_time,
604     TYPE_INTEGER,
605   },
606   {
607     GAME_PANEL_GRAVITY_STATE,
608     &game.panel.gravity_state,
609     TYPE_STRING,
610   },
611   {
612     GAME_PANEL_GRAPHIC_1,
613     &game.panel.graphic[0],
614     TYPE_ELEMENT,
615   },
616   {
617     GAME_PANEL_GRAPHIC_2,
618     &game.panel.graphic[1],
619     TYPE_ELEMENT,
620   },
621   {
622     GAME_PANEL_GRAPHIC_3,
623     &game.panel.graphic[2],
624     TYPE_ELEMENT,
625   },
626   {
627     GAME_PANEL_GRAPHIC_4,
628     &game.panel.graphic[3],
629     TYPE_ELEMENT,
630   },
631   {
632     GAME_PANEL_GRAPHIC_5,
633     &game.panel.graphic[4],
634     TYPE_ELEMENT,
635   },
636   {
637     GAME_PANEL_GRAPHIC_6,
638     &game.panel.graphic[5],
639     TYPE_ELEMENT,
640   },
641   {
642     GAME_PANEL_GRAPHIC_7,
643     &game.panel.graphic[6],
644     TYPE_ELEMENT,
645   },
646   {
647     GAME_PANEL_GRAPHIC_8,
648     &game.panel.graphic[7],
649     TYPE_ELEMENT,
650   },
651   {
652     GAME_PANEL_ELEMENT_1,
653     &game.panel.element[0],
654     TYPE_ELEMENT,
655   },
656   {
657     GAME_PANEL_ELEMENT_2,
658     &game.panel.element[1],
659     TYPE_ELEMENT,
660   },
661   {
662     GAME_PANEL_ELEMENT_3,
663     &game.panel.element[2],
664     TYPE_ELEMENT,
665   },
666   {
667     GAME_PANEL_ELEMENT_4,
668     &game.panel.element[3],
669     TYPE_ELEMENT,
670   },
671   {
672     GAME_PANEL_ELEMENT_5,
673     &game.panel.element[4],
674     TYPE_ELEMENT,
675   },
676   {
677     GAME_PANEL_ELEMENT_6,
678     &game.panel.element[5],
679     TYPE_ELEMENT,
680   },
681   {
682     GAME_PANEL_ELEMENT_7,
683     &game.panel.element[6],
684     TYPE_ELEMENT,
685   },
686   {
687     GAME_PANEL_ELEMENT_8,
688     &game.panel.element[7],
689     TYPE_ELEMENT,
690   },
691   {
692     GAME_PANEL_ELEMENT_COUNT_1,
693     &game.panel.element_count[0],
694     TYPE_INTEGER,
695   },
696   {
697     GAME_PANEL_ELEMENT_COUNT_2,
698     &game.panel.element_count[1],
699     TYPE_INTEGER,
700   },
701   {
702     GAME_PANEL_ELEMENT_COUNT_3,
703     &game.panel.element_count[2],
704     TYPE_INTEGER,
705   },
706   {
707     GAME_PANEL_ELEMENT_COUNT_4,
708     &game.panel.element_count[3],
709     TYPE_INTEGER,
710   },
711   {
712     GAME_PANEL_ELEMENT_COUNT_5,
713     &game.panel.element_count[4],
714     TYPE_INTEGER,
715   },
716   {
717     GAME_PANEL_ELEMENT_COUNT_6,
718     &game.panel.element_count[5],
719     TYPE_INTEGER,
720   },
721   {
722     GAME_PANEL_ELEMENT_COUNT_7,
723     &game.panel.element_count[6],
724     TYPE_INTEGER,
725   },
726   {
727     GAME_PANEL_ELEMENT_COUNT_8,
728     &game.panel.element_count[7],
729     TYPE_INTEGER,
730   },
731   {
732     GAME_PANEL_CE_SCORE_1,
733     &game.panel.ce_score[0],
734     TYPE_INTEGER,
735   },
736   {
737     GAME_PANEL_CE_SCORE_2,
738     &game.panel.ce_score[1],
739     TYPE_INTEGER,
740   },
741   {
742     GAME_PANEL_CE_SCORE_3,
743     &game.panel.ce_score[2],
744     TYPE_INTEGER,
745   },
746   {
747     GAME_PANEL_CE_SCORE_4,
748     &game.panel.ce_score[3],
749     TYPE_INTEGER,
750   },
751   {
752     GAME_PANEL_CE_SCORE_5,
753     &game.panel.ce_score[4],
754     TYPE_INTEGER,
755   },
756   {
757     GAME_PANEL_CE_SCORE_6,
758     &game.panel.ce_score[5],
759     TYPE_INTEGER,
760   },
761   {
762     GAME_PANEL_CE_SCORE_7,
763     &game.panel.ce_score[6],
764     TYPE_INTEGER,
765   },
766   {
767     GAME_PANEL_CE_SCORE_8,
768     &game.panel.ce_score[7],
769     TYPE_INTEGER,
770   },
771   {
772     GAME_PANEL_CE_SCORE_1_ELEMENT,
773     &game.panel.ce_score_element[0],
774     TYPE_ELEMENT,
775   },
776   {
777     GAME_PANEL_CE_SCORE_2_ELEMENT,
778     &game.panel.ce_score_element[1],
779     TYPE_ELEMENT,
780   },
781   {
782     GAME_PANEL_CE_SCORE_3_ELEMENT,
783     &game.panel.ce_score_element[2],
784     TYPE_ELEMENT,
785   },
786   {
787     GAME_PANEL_CE_SCORE_4_ELEMENT,
788     &game.panel.ce_score_element[3],
789     TYPE_ELEMENT,
790   },
791   {
792     GAME_PANEL_CE_SCORE_5_ELEMENT,
793     &game.panel.ce_score_element[4],
794     TYPE_ELEMENT,
795   },
796   {
797     GAME_PANEL_CE_SCORE_6_ELEMENT,
798     &game.panel.ce_score_element[5],
799     TYPE_ELEMENT,
800   },
801   {
802     GAME_PANEL_CE_SCORE_7_ELEMENT,
803     &game.panel.ce_score_element[6],
804     TYPE_ELEMENT,
805   },
806   {
807     GAME_PANEL_CE_SCORE_8_ELEMENT,
808     &game.panel.ce_score_element[7],
809     TYPE_ELEMENT,
810   },
811   {
812     GAME_PANEL_PLAYER_NAME,
813     &game.panel.player_name,
814     TYPE_STRING,
815   },
816   {
817     GAME_PANEL_LEVEL_NAME,
818     &game.panel.level_name,
819     TYPE_STRING,
820   },
821   {
822     GAME_PANEL_LEVEL_AUTHOR,
823     &game.panel.level_author,
824     TYPE_STRING,
825   },
826
827   {
828     -1,
829     NULL,
830     -1,
831   }
832 };
833
834 // values for delayed check of falling and moving elements and for collision
835 #define CHECK_DELAY_MOVING      3
836 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
837 #define CHECK_DELAY_COLLISION   2
838 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
839
840 // values for initial player move delay (initial delay counter value)
841 #define INITIAL_MOVE_DELAY_OFF  -1
842 #define INITIAL_MOVE_DELAY_ON   0
843
844 // values for player movement speed (which is in fact a delay value)
845 #define MOVE_DELAY_MIN_SPEED    32
846 #define MOVE_DELAY_NORMAL_SPEED 8
847 #define MOVE_DELAY_HIGH_SPEED   4
848 #define MOVE_DELAY_MAX_SPEED    1
849
850 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
851 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
852
853 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
854 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
855
856 // values for scroll positions
857 #define SCROLL_POSITION_X(x)    ((x) < SBX_Left  + MIDPOSX ? SBX_Left : \
858                                  (x) > SBX_Right + MIDPOSX ? SBX_Right :\
859                                  (x) - MIDPOSX)
860 #define SCROLL_POSITION_Y(y)    ((y) < SBY_Upper + MIDPOSY ? SBY_Upper :\
861                                  (y) > SBY_Lower + MIDPOSY ? SBY_Lower :\
862                                  (y) - MIDPOSY)
863
864 // values for other actions
865 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
866 #define MOVE_STEPSIZE_MIN       (1)
867 #define MOVE_STEPSIZE_MAX       (TILEX)
868
869 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
870 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
871
872 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
873
874 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
875                                  RND(element_info[e].push_delay_random))
876 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
877                                  RND(element_info[e].drop_delay_random))
878 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
879                                  RND(element_info[e].move_delay_random))
880 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
881                                     (element_info[e].move_delay_random))
882 #define GET_NEW_STEP_DELAY(e)   (   (element_info[e].step_delay_fixed) + \
883                                  RND(element_info[e].step_delay_random))
884 #define GET_MAX_STEP_DELAY(e)   (   (element_info[e].step_delay_fixed) + \
885                                     (element_info[e].step_delay_random))
886 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
887                                  RND(element_info[e].ce_value_random_initial))
888 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
889 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
890                                  RND((c)->delay_random * (c)->delay_frames))
891 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
892                                  RND((c)->delay_random))
893
894
895 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
896          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
897
898 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
899         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
900          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
901          (be) + (e) - EL_SELF)
902
903 #define GET_PLAYER_FROM_BITS(p)                                         \
904         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
905
906 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
907         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
908          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
909          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
910          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
911          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
912          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
913          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
914          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
915          (e))
916
917 #define CAN_GROW_INTO(e)                                                \
918         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
919
920 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
921                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
922                                         (condition)))
923
924 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
925                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
926                                         (CAN_MOVE_INTO_ACID(e) &&       \
927                                          Tile[x][y] == EL_ACID) ||      \
928                                         (condition)))
929
930 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
931                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
932                                         (CAN_MOVE_INTO_ACID(e) &&       \
933                                          Tile[x][y] == EL_ACID) ||      \
934                                         (condition)))
935
936 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
937                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
938                                         (condition) ||                  \
939                                         (CAN_MOVE_INTO_ACID(e) &&       \
940                                          Tile[x][y] == EL_ACID) ||      \
941                                         (DONT_COLLIDE_WITH(e) &&        \
942                                          IS_PLAYER(x, y) &&             \
943                                          !PLAYER_ENEMY_PROTECTED(x, y))))
944
945 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
946         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
947
948 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
949         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
950
951 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
952         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Tile[x][y] == EL_EMC_PLANT)
953
954 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
955         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Tile[x][y]) || \
956                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
957
958 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
959         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
960
961 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
962         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Tile[x][y] == EL_DIAMOND)
963
964 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
965         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_FOOD_DARK_YAMYAM(Tile[x][y]))
966
967 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
968         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Tile[x][y]))
969
970 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
971         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Tile[x][y]))
972
973 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
974         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Tile[x][y] == EL_EXIT_OPEN || \
975                                                  Tile[x][y] == EL_EM_EXIT_OPEN || \
976                                                  Tile[x][y] == EL_STEEL_EXIT_OPEN || \
977                                                  Tile[x][y] == EL_EM_STEEL_EXIT_OPEN || \
978                                                  IS_FOOD_PENGUIN(Tile[x][y])))
979 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
980         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
981
982 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
983         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
984
985 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
986         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
987
988 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
989         (IN_LEV_FIELD(x, y) && (Tile[x][y] == EL_EMC_SPRING_BUMPER ||   \
990                                 Tile[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
991
992 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
993
994 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
995                 (!IS_PLAYER(x, y) &&                                    \
996                  IS_EQUAL_OR_IN_GROUP(Tile[x][y], MOVE_ENTER_EL(e)))
997
998 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
999         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
1000
1001 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
1002 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
1003
1004 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
1005 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
1006 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
1007 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1008
1009 #define MM_HEALTH(x)            (MIN(MAX(0, MAX_HEALTH - (x)), MAX_HEALTH))
1010
1011 // game button identifiers
1012 #define GAME_CTRL_ID_STOP               0
1013 #define GAME_CTRL_ID_PAUSE              1
1014 #define GAME_CTRL_ID_PLAY               2
1015 #define GAME_CTRL_ID_UNDO               3
1016 #define GAME_CTRL_ID_REDO               4
1017 #define GAME_CTRL_ID_SAVE               5
1018 #define GAME_CTRL_ID_PAUSE2             6
1019 #define GAME_CTRL_ID_LOAD               7
1020 #define GAME_CTRL_ID_PANEL_STOP         8
1021 #define GAME_CTRL_ID_PANEL_PAUSE        9
1022 #define GAME_CTRL_ID_PANEL_PLAY         10
1023 #define GAME_CTRL_ID_TOUCH_STOP         11
1024 #define GAME_CTRL_ID_TOUCH_PAUSE        12
1025 #define SOUND_CTRL_ID_MUSIC             13
1026 #define SOUND_CTRL_ID_LOOPS             14
1027 #define SOUND_CTRL_ID_SIMPLE            15
1028 #define SOUND_CTRL_ID_PANEL_MUSIC       16
1029 #define SOUND_CTRL_ID_PANEL_LOOPS       17
1030 #define SOUND_CTRL_ID_PANEL_SIMPLE      18
1031
1032 #define NUM_GAME_BUTTONS                19
1033
1034
1035 // forward declaration for internal use
1036
1037 static void CreateField(int, int, int);
1038
1039 static void ResetGfxAnimation(int, int);
1040
1041 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1042 static void AdvanceFrameAndPlayerCounters(int);
1043
1044 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1045 static boolean MovePlayer(struct PlayerInfo *, int, int);
1046 static void ScrollPlayer(struct PlayerInfo *, int);
1047 static void ScrollScreen(struct PlayerInfo *, int);
1048
1049 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1050 static boolean DigFieldByCE(int, int, int);
1051 static boolean SnapField(struct PlayerInfo *, int, int);
1052 static boolean DropElement(struct PlayerInfo *);
1053
1054 static void InitBeltMovement(void);
1055 static void CloseAllOpenTimegates(void);
1056 static void CheckGravityMovement(struct PlayerInfo *);
1057 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1058 static void KillPlayerUnlessEnemyProtected(int, int);
1059 static void KillPlayerUnlessExplosionProtected(int, int);
1060
1061 static void CheckNextToConditions(int, int);
1062 static void TestIfPlayerNextToCustomElement(int, int);
1063 static void TestIfPlayerTouchesCustomElement(int, int);
1064 static void TestIfElementNextToCustomElement(int, int);
1065 static void TestIfElementTouchesCustomElement(int, int);
1066 static void TestIfElementHitsCustomElement(int, int, int);
1067
1068 static void HandleElementChange(int, int, int);
1069 static void ExecuteCustomElementAction(int, int, int, int);
1070 static boolean ChangeElement(int, int, int, int);
1071
1072 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int, int, int);
1073 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1074         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, -1)
1075 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1076         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1077 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1078         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1079 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1080         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1081 #define CheckTriggeredElementChangeByMouse(x, y, e, ev, s)              \
1082         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1083
1084 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1085 #define CheckElementChange(x, y, e, te, ev)                             \
1086         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1087 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1088         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1089 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1090         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1091 #define CheckElementChangeByMouse(x, y, e, ev, s)                       \
1092         CheckElementChangeExt(x, y, e, EL_UNDEFINED, ev, CH_PLAYER_ANY, s)
1093
1094 static void PlayLevelSound(int, int, int);
1095 static void PlayLevelSoundNearest(int, int, int);
1096 static void PlayLevelSoundAction(int, int, int);
1097 static void PlayLevelSoundElementAction(int, int, int, int);
1098 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1099 static void PlayLevelSoundActionIfLoop(int, int, int);
1100 static void StopLevelSoundActionIfLoop(int, int, int);
1101 static void PlayLevelMusic(void);
1102 static void FadeLevelSoundsAndMusic(void);
1103
1104 static void HandleGameButtons(struct GadgetInfo *);
1105
1106 int AmoebaNeighbourNr(int, int);
1107 void AmoebaToDiamond(int, int);
1108 void ContinueMoving(int, int);
1109 void Bang(int, int);
1110 void InitMovDir(int, int);
1111 void InitAmoebaNr(int, int);
1112 void NewHighScore(int, boolean);
1113
1114 void TestIfGoodThingHitsBadThing(int, int, int);
1115 void TestIfBadThingHitsGoodThing(int, int, int);
1116 void TestIfPlayerTouchesBadThing(int, int);
1117 void TestIfPlayerRunsIntoBadThing(int, int, int);
1118 void TestIfBadThingTouchesPlayer(int, int);
1119 void TestIfBadThingRunsIntoPlayer(int, int, int);
1120 void TestIfFriendTouchesBadThing(int, int);
1121 void TestIfBadThingTouchesFriend(int, int);
1122 void TestIfBadThingTouchesOtherBadThing(int, int);
1123 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1124
1125 void KillPlayer(struct PlayerInfo *);
1126 void BuryPlayer(struct PlayerInfo *);
1127 void RemovePlayer(struct PlayerInfo *);
1128 void ExitPlayer(struct PlayerInfo *);
1129
1130 static int getInvisibleActiveFromInvisibleElement(int);
1131 static int getInvisibleFromInvisibleActiveElement(int);
1132
1133 static void TestFieldAfterSnapping(int, int, int, int, int);
1134
1135 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1136
1137 // for detection of endless loops, caused by custom element programming
1138 // (using maximal playfield width x 10 is just a rough approximation)
1139 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1140
1141 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1142 {                                                                       \
1143   if (recursion_loop_detected)                                          \
1144     return (rc);                                                        \
1145                                                                         \
1146   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1147   {                                                                     \
1148     recursion_loop_detected = TRUE;                                     \
1149     recursion_loop_element = (e);                                       \
1150   }                                                                     \
1151                                                                         \
1152   recursion_loop_depth++;                                               \
1153 }
1154
1155 #define RECURSION_LOOP_DETECTION_END()                                  \
1156 {                                                                       \
1157   recursion_loop_depth--;                                               \
1158 }
1159
1160 static int recursion_loop_depth;
1161 static boolean recursion_loop_detected;
1162 static boolean recursion_loop_element;
1163
1164 static int map_player_action[MAX_PLAYERS];
1165
1166
1167 // ----------------------------------------------------------------------------
1168 // definition of elements that automatically change to other elements after
1169 // a specified time, eventually calling a function when changing
1170 // ----------------------------------------------------------------------------
1171
1172 // forward declaration for changer functions
1173 static void InitBuggyBase(int, int);
1174 static void WarnBuggyBase(int, int);
1175
1176 static void InitTrap(int, int);
1177 static void ActivateTrap(int, int);
1178 static void ChangeActiveTrap(int, int);
1179
1180 static void InitRobotWheel(int, int);
1181 static void RunRobotWheel(int, int);
1182 static void StopRobotWheel(int, int);
1183
1184 static void InitTimegateWheel(int, int);
1185 static void RunTimegateWheel(int, int);
1186
1187 static void InitMagicBallDelay(int, int);
1188 static void ActivateMagicBall(int, int);
1189
1190 struct ChangingElementInfo
1191 {
1192   int element;
1193   int target_element;
1194   int change_delay;
1195   void (*pre_change_function)(int x, int y);
1196   void (*change_function)(int x, int y);
1197   void (*post_change_function)(int x, int y);
1198 };
1199
1200 static struct ChangingElementInfo change_delay_list[] =
1201 {
1202   {
1203     EL_NUT_BREAKING,
1204     EL_EMERALD,
1205     6,
1206     NULL,
1207     NULL,
1208     NULL
1209   },
1210   {
1211     EL_PEARL_BREAKING,
1212     EL_EMPTY,
1213     8,
1214     NULL,
1215     NULL,
1216     NULL
1217   },
1218   {
1219     EL_EXIT_OPENING,
1220     EL_EXIT_OPEN,
1221     29,
1222     NULL,
1223     NULL,
1224     NULL
1225   },
1226   {
1227     EL_EXIT_CLOSING,
1228     EL_EXIT_CLOSED,
1229     29,
1230     NULL,
1231     NULL,
1232     NULL
1233   },
1234   {
1235     EL_STEEL_EXIT_OPENING,
1236     EL_STEEL_EXIT_OPEN,
1237     29,
1238     NULL,
1239     NULL,
1240     NULL
1241   },
1242   {
1243     EL_STEEL_EXIT_CLOSING,
1244     EL_STEEL_EXIT_CLOSED,
1245     29,
1246     NULL,
1247     NULL,
1248     NULL
1249   },
1250   {
1251     EL_EM_EXIT_OPENING,
1252     EL_EM_EXIT_OPEN,
1253     29,
1254     NULL,
1255     NULL,
1256     NULL
1257   },
1258   {
1259     EL_EM_EXIT_CLOSING,
1260     EL_EMPTY,
1261     29,
1262     NULL,
1263     NULL,
1264     NULL
1265   },
1266   {
1267     EL_EM_STEEL_EXIT_OPENING,
1268     EL_EM_STEEL_EXIT_OPEN,
1269     29,
1270     NULL,
1271     NULL,
1272     NULL
1273   },
1274   {
1275     EL_EM_STEEL_EXIT_CLOSING,
1276     EL_STEELWALL,
1277     29,
1278     NULL,
1279     NULL,
1280     NULL
1281   },
1282   {
1283     EL_SP_EXIT_OPENING,
1284     EL_SP_EXIT_OPEN,
1285     29,
1286     NULL,
1287     NULL,
1288     NULL
1289   },
1290   {
1291     EL_SP_EXIT_CLOSING,
1292     EL_SP_EXIT_CLOSED,
1293     29,
1294     NULL,
1295     NULL,
1296     NULL
1297   },
1298   {
1299     EL_SWITCHGATE_OPENING,
1300     EL_SWITCHGATE_OPEN,
1301     29,
1302     NULL,
1303     NULL,
1304     NULL
1305   },
1306   {
1307     EL_SWITCHGATE_CLOSING,
1308     EL_SWITCHGATE_CLOSED,
1309     29,
1310     NULL,
1311     NULL,
1312     NULL
1313   },
1314   {
1315     EL_TIMEGATE_OPENING,
1316     EL_TIMEGATE_OPEN,
1317     29,
1318     NULL,
1319     NULL,
1320     NULL
1321   },
1322   {
1323     EL_TIMEGATE_CLOSING,
1324     EL_TIMEGATE_CLOSED,
1325     29,
1326     NULL,
1327     NULL,
1328     NULL
1329   },
1330
1331   {
1332     EL_ACID_SPLASH_LEFT,
1333     EL_EMPTY,
1334     8,
1335     NULL,
1336     NULL,
1337     NULL
1338   },
1339   {
1340     EL_ACID_SPLASH_RIGHT,
1341     EL_EMPTY,
1342     8,
1343     NULL,
1344     NULL,
1345     NULL
1346   },
1347   {
1348     EL_SP_BUGGY_BASE,
1349     EL_SP_BUGGY_BASE_ACTIVATING,
1350     0,
1351     InitBuggyBase,
1352     NULL,
1353     NULL
1354   },
1355   {
1356     EL_SP_BUGGY_BASE_ACTIVATING,
1357     EL_SP_BUGGY_BASE_ACTIVE,
1358     0,
1359     InitBuggyBase,
1360     NULL,
1361     NULL
1362   },
1363   {
1364     EL_SP_BUGGY_BASE_ACTIVE,
1365     EL_SP_BUGGY_BASE,
1366     0,
1367     InitBuggyBase,
1368     WarnBuggyBase,
1369     NULL
1370   },
1371   {
1372     EL_TRAP,
1373     EL_TRAP_ACTIVE,
1374     0,
1375     InitTrap,
1376     NULL,
1377     ActivateTrap
1378   },
1379   {
1380     EL_TRAP_ACTIVE,
1381     EL_TRAP,
1382     31,
1383     NULL,
1384     ChangeActiveTrap,
1385     NULL
1386   },
1387   {
1388     EL_ROBOT_WHEEL_ACTIVE,
1389     EL_ROBOT_WHEEL,
1390     0,
1391     InitRobotWheel,
1392     RunRobotWheel,
1393     StopRobotWheel
1394   },
1395   {
1396     EL_TIMEGATE_SWITCH_ACTIVE,
1397     EL_TIMEGATE_SWITCH,
1398     0,
1399     InitTimegateWheel,
1400     RunTimegateWheel,
1401     NULL
1402   },
1403   {
1404     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1405     EL_DC_TIMEGATE_SWITCH,
1406     0,
1407     InitTimegateWheel,
1408     RunTimegateWheel,
1409     NULL
1410   },
1411   {
1412     EL_EMC_MAGIC_BALL_ACTIVE,
1413     EL_EMC_MAGIC_BALL_ACTIVE,
1414     0,
1415     InitMagicBallDelay,
1416     NULL,
1417     ActivateMagicBall
1418   },
1419   {
1420     EL_EMC_SPRING_BUMPER_ACTIVE,
1421     EL_EMC_SPRING_BUMPER,
1422     8,
1423     NULL,
1424     NULL,
1425     NULL
1426   },
1427   {
1428     EL_DIAGONAL_SHRINKING,
1429     EL_UNDEFINED,
1430     0,
1431     NULL,
1432     NULL,
1433     NULL
1434   },
1435   {
1436     EL_DIAGONAL_GROWING,
1437     EL_UNDEFINED,
1438     0,
1439     NULL,
1440     NULL,
1441     NULL,
1442   },
1443
1444   {
1445     EL_UNDEFINED,
1446     EL_UNDEFINED,
1447     -1,
1448     NULL,
1449     NULL,
1450     NULL
1451   }
1452 };
1453
1454 struct
1455 {
1456   int element;
1457   int push_delay_fixed, push_delay_random;
1458 }
1459 push_delay_list[] =
1460 {
1461   { EL_SPRING,                  0, 0 },
1462   { EL_BALLOON,                 0, 0 },
1463
1464   { EL_SOKOBAN_OBJECT,          2, 0 },
1465   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1466   { EL_SATELLITE,               2, 0 },
1467   { EL_SP_DISK_YELLOW,          2, 0 },
1468
1469   { EL_UNDEFINED,               0, 0 },
1470 };
1471
1472 struct
1473 {
1474   int element;
1475   int move_stepsize;
1476 }
1477 move_stepsize_list[] =
1478 {
1479   { EL_AMOEBA_DROP,             2 },
1480   { EL_AMOEBA_DROPPING,         2 },
1481   { EL_QUICKSAND_FILLING,       1 },
1482   { EL_QUICKSAND_EMPTYING,      1 },
1483   { EL_QUICKSAND_FAST_FILLING,  2 },
1484   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1485   { EL_MAGIC_WALL_FILLING,      2 },
1486   { EL_MAGIC_WALL_EMPTYING,     2 },
1487   { EL_BD_MAGIC_WALL_FILLING,   2 },
1488   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1489   { EL_DC_MAGIC_WALL_FILLING,   2 },
1490   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1491
1492   { EL_UNDEFINED,               0 },
1493 };
1494
1495 struct
1496 {
1497   int element;
1498   int count;
1499 }
1500 collect_count_list[] =
1501 {
1502   { EL_EMERALD,                 1 },
1503   { EL_BD_DIAMOND,              1 },
1504   { EL_EMERALD_YELLOW,          1 },
1505   { EL_EMERALD_RED,             1 },
1506   { EL_EMERALD_PURPLE,          1 },
1507   { EL_DIAMOND,                 3 },
1508   { EL_SP_INFOTRON,             1 },
1509   { EL_PEARL,                   5 },
1510   { EL_CRYSTAL,                 8 },
1511
1512   { EL_UNDEFINED,               0 },
1513 };
1514
1515 struct
1516 {
1517   int element;
1518   int direction;
1519 }
1520 access_direction_list[] =
1521 {
1522   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1523   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1524   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1525   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1526   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1527   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1528   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1529   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1530   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1531   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1532   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1533
1534   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1535   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1536   { EL_SP_PORT_UP,                                                   MV_DOWN },
1537   { EL_SP_PORT_DOWN,                                         MV_UP           },
1538   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1539   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1540   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1541   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1542   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1543   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1544   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1545   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1546   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1547   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1548   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1549   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1550   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1551   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1552   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1553
1554   { EL_UNDEFINED,                       MV_NONE                              }
1555 };
1556
1557 static struct XY xy_topdown[] =
1558 {
1559   {  0, -1 },
1560   { -1,  0 },
1561   { +1,  0 },
1562   {  0, +1 }
1563 };
1564
1565 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1566
1567 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1568 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1569 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Tile[x][y]) || \
1570                                  IS_JUST_CHANGING(x, y))
1571
1572 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1573
1574 // static variables for playfield scan mode (scanning forward or backward)
1575 static int playfield_scan_start_x = 0;
1576 static int playfield_scan_start_y = 0;
1577 static int playfield_scan_delta_x = 1;
1578 static int playfield_scan_delta_y = 1;
1579
1580 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1581                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1582                                      (y) += playfield_scan_delta_y)     \
1583                                 for ((x) = playfield_scan_start_x;      \
1584                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1585                                      (x) += playfield_scan_delta_x)
1586
1587 #ifdef DEBUG
1588 void DEBUG_SetMaximumDynamite(void)
1589 {
1590   int i;
1591
1592   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1593     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1594       local_player->inventory_element[local_player->inventory_size++] =
1595         EL_DYNAMITE;
1596 }
1597 #endif
1598
1599 static void InitPlayfieldScanModeVars(void)
1600 {
1601   if (game.use_reverse_scan_direction)
1602   {
1603     playfield_scan_start_x = lev_fieldx - 1;
1604     playfield_scan_start_y = lev_fieldy - 1;
1605
1606     playfield_scan_delta_x = -1;
1607     playfield_scan_delta_y = -1;
1608   }
1609   else
1610   {
1611     playfield_scan_start_x = 0;
1612     playfield_scan_start_y = 0;
1613
1614     playfield_scan_delta_x = 1;
1615     playfield_scan_delta_y = 1;
1616   }
1617 }
1618
1619 static void InitPlayfieldScanMode(int mode)
1620 {
1621   game.use_reverse_scan_direction =
1622     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1623
1624   InitPlayfieldScanModeVars();
1625 }
1626
1627 static int get_move_delay_from_stepsize(int move_stepsize)
1628 {
1629   move_stepsize =
1630     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1631
1632   // make sure that stepsize value is always a power of 2
1633   move_stepsize = (1 << log_2(move_stepsize));
1634
1635   return TILEX / move_stepsize;
1636 }
1637
1638 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1639                                boolean init_game)
1640 {
1641   int player_nr = player->index_nr;
1642   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1643   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1644
1645   // do no immediately change move delay -- the player might just be moving
1646   player->move_delay_value_next = move_delay;
1647
1648   // information if player can move must be set separately
1649   player->cannot_move = cannot_move;
1650
1651   if (init_game)
1652   {
1653     player->move_delay       = game.initial_move_delay[player_nr];
1654     player->move_delay_value = game.initial_move_delay_value[player_nr];
1655
1656     player->move_delay_value_next = -1;
1657
1658     player->move_delay_reset_counter = 0;
1659   }
1660 }
1661
1662 void GetPlayerConfig(void)
1663 {
1664   GameFrameDelay = setup.game_frame_delay;
1665
1666   if (!audio.sound_available)
1667     setup.sound_simple = FALSE;
1668
1669   if (!audio.loops_available)
1670     setup.sound_loops = FALSE;
1671
1672   if (!audio.music_available)
1673     setup.sound_music = FALSE;
1674
1675   if (!video.fullscreen_available)
1676     setup.fullscreen = FALSE;
1677
1678   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1679
1680   SetAudioMode(setup.sound);
1681 }
1682
1683 int GetElementFromGroupElement(int element)
1684 {
1685   if (IS_GROUP_ELEMENT(element))
1686   {
1687     struct ElementGroupInfo *group = element_info[element].group;
1688     int last_anim_random_frame = gfx.anim_random_frame;
1689     int element_pos;
1690
1691     if (group->choice_mode == ANIM_RANDOM)
1692       gfx.anim_random_frame = RND(group->num_elements_resolved);
1693
1694     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1695                                     group->choice_mode, 0,
1696                                     group->choice_pos);
1697
1698     if (group->choice_mode == ANIM_RANDOM)
1699       gfx.anim_random_frame = last_anim_random_frame;
1700
1701     group->choice_pos++;
1702
1703     element = group->element_resolved[element_pos];
1704   }
1705
1706   return element;
1707 }
1708
1709 static void IncrementSokobanFieldsNeeded(void)
1710 {
1711   if (level.sb_fields_needed)
1712     game.sokoban_fields_still_needed++;
1713 }
1714
1715 static void IncrementSokobanObjectsNeeded(void)
1716 {
1717   if (level.sb_objects_needed)
1718     game.sokoban_objects_still_needed++;
1719 }
1720
1721 static void DecrementSokobanFieldsNeeded(void)
1722 {
1723   if (game.sokoban_fields_still_needed > 0)
1724     game.sokoban_fields_still_needed--;
1725 }
1726
1727 static void DecrementSokobanObjectsNeeded(void)
1728 {
1729   if (game.sokoban_objects_still_needed > 0)
1730     game.sokoban_objects_still_needed--;
1731 }
1732
1733 static void InitPlayerField(int x, int y, int element, boolean init_game)
1734 {
1735   if (element == EL_SP_MURPHY)
1736   {
1737     if (init_game)
1738     {
1739       if (stored_player[0].present)
1740       {
1741         Tile[x][y] = EL_SP_MURPHY_CLONE;
1742
1743         return;
1744       }
1745       else
1746       {
1747         stored_player[0].initial_element = element;
1748         stored_player[0].use_murphy = TRUE;
1749
1750         if (!level.use_artwork_element[0])
1751           stored_player[0].artwork_element = EL_SP_MURPHY;
1752       }
1753
1754       Tile[x][y] = EL_PLAYER_1;
1755     }
1756   }
1757
1758   if (init_game)
1759   {
1760     struct PlayerInfo *player = &stored_player[Tile[x][y] - EL_PLAYER_1];
1761     int jx = player->jx, jy = player->jy;
1762
1763     player->present = TRUE;
1764
1765     player->block_last_field = (element == EL_SP_MURPHY ?
1766                                 level.sp_block_last_field :
1767                                 level.block_last_field);
1768
1769     // ---------- initialize player's last field block delay ------------------
1770
1771     // always start with reliable default value (no adjustment needed)
1772     player->block_delay_adjustment = 0;
1773
1774     // special case 1: in Supaplex, Murphy blocks last field one more frame
1775     if (player->block_last_field && element == EL_SP_MURPHY)
1776       player->block_delay_adjustment = 1;
1777
1778     // special case 2: in game engines before 3.1.1, blocking was different
1779     if (game.use_block_last_field_bug)
1780       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1781
1782     if (!network.enabled || player->connected_network)
1783     {
1784       player->active = TRUE;
1785
1786       // remove potentially duplicate players
1787       if (IN_LEV_FIELD(jx, jy) && StorePlayer[jx][jy] == Tile[x][y])
1788         StorePlayer[jx][jy] = 0;
1789
1790       StorePlayer[x][y] = Tile[x][y];
1791
1792 #if DEBUG_INIT_PLAYER
1793       Debug("game:init:player", "- player element %d activated",
1794             player->element_nr);
1795       Debug("game:init:player", "  (local player is %d and currently %s)",
1796             local_player->element_nr,
1797             local_player->active ? "active" : "not active");
1798     }
1799 #endif
1800
1801     Tile[x][y] = EL_EMPTY;
1802
1803     player->jx = player->last_jx = x;
1804     player->jy = player->last_jy = y;
1805   }
1806
1807   // always check if player was just killed and should be reanimated
1808   {
1809     int player_nr = GET_PLAYER_NR(element);
1810     struct PlayerInfo *player = &stored_player[player_nr];
1811
1812     if (player->active && player->killed)
1813       player->reanimated = TRUE; // if player was just killed, reanimate him
1814   }
1815 }
1816
1817 static void InitField(int x, int y, boolean init_game)
1818 {
1819   int element = Tile[x][y];
1820
1821   switch (element)
1822   {
1823     case EL_SP_MURPHY:
1824     case EL_PLAYER_1:
1825     case EL_PLAYER_2:
1826     case EL_PLAYER_3:
1827     case EL_PLAYER_4:
1828       InitPlayerField(x, y, element, init_game);
1829       break;
1830
1831     case EL_SOKOBAN_FIELD_PLAYER:
1832       element = Tile[x][y] = EL_PLAYER_1;
1833       InitField(x, y, init_game);
1834
1835       element = Tile[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1836       InitField(x, y, init_game);
1837       break;
1838
1839     case EL_SOKOBAN_FIELD_EMPTY:
1840       IncrementSokobanFieldsNeeded();
1841       break;
1842
1843     case EL_SOKOBAN_OBJECT:
1844       IncrementSokobanObjectsNeeded();
1845       break;
1846
1847     case EL_STONEBLOCK:
1848       if (x < lev_fieldx - 1 && Tile[x + 1][y] == EL_ACID)
1849         Tile[x][y] = EL_ACID_POOL_TOPLEFT;
1850       else if (x > 0 && Tile[x - 1][y] == EL_ACID)
1851         Tile[x][y] = EL_ACID_POOL_TOPRIGHT;
1852       else if (y > 0 && Tile[x][y - 1] == EL_ACID_POOL_TOPLEFT)
1853         Tile[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1854       else if (y > 0 && Tile[x][y - 1] == EL_ACID)
1855         Tile[x][y] = EL_ACID_POOL_BOTTOM;
1856       else if (y > 0 && Tile[x][y - 1] == EL_ACID_POOL_TOPRIGHT)
1857         Tile[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1858       break;
1859
1860     case EL_BUG:
1861     case EL_BUG_RIGHT:
1862     case EL_BUG_UP:
1863     case EL_BUG_LEFT:
1864     case EL_BUG_DOWN:
1865     case EL_SPACESHIP:
1866     case EL_SPACESHIP_RIGHT:
1867     case EL_SPACESHIP_UP:
1868     case EL_SPACESHIP_LEFT:
1869     case EL_SPACESHIP_DOWN:
1870     case EL_BD_BUTTERFLY:
1871     case EL_BD_BUTTERFLY_RIGHT:
1872     case EL_BD_BUTTERFLY_UP:
1873     case EL_BD_BUTTERFLY_LEFT:
1874     case EL_BD_BUTTERFLY_DOWN:
1875     case EL_BD_FIREFLY:
1876     case EL_BD_FIREFLY_RIGHT:
1877     case EL_BD_FIREFLY_UP:
1878     case EL_BD_FIREFLY_LEFT:
1879     case EL_BD_FIREFLY_DOWN:
1880     case EL_PACMAN_RIGHT:
1881     case EL_PACMAN_UP:
1882     case EL_PACMAN_LEFT:
1883     case EL_PACMAN_DOWN:
1884     case EL_YAMYAM:
1885     case EL_YAMYAM_LEFT:
1886     case EL_YAMYAM_RIGHT:
1887     case EL_YAMYAM_UP:
1888     case EL_YAMYAM_DOWN:
1889     case EL_DARK_YAMYAM:
1890     case EL_ROBOT:
1891     case EL_PACMAN:
1892     case EL_SP_SNIKSNAK:
1893     case EL_SP_ELECTRON:
1894     case EL_MOLE:
1895     case EL_MOLE_LEFT:
1896     case EL_MOLE_RIGHT:
1897     case EL_MOLE_UP:
1898     case EL_MOLE_DOWN:
1899     case EL_SPRING_LEFT:
1900     case EL_SPRING_RIGHT:
1901       InitMovDir(x, y);
1902       break;
1903
1904     case EL_AMOEBA_FULL:
1905     case EL_BD_AMOEBA:
1906       InitAmoebaNr(x, y);
1907       break;
1908
1909     case EL_AMOEBA_DROP:
1910       if (y == lev_fieldy - 1)
1911       {
1912         Tile[x][y] = EL_AMOEBA_GROWING;
1913         Store[x][y] = EL_AMOEBA_WET;
1914       }
1915       break;
1916
1917     case EL_DYNAMITE_ACTIVE:
1918     case EL_SP_DISK_RED_ACTIVE:
1919     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1920     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1921     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1922     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1923       MovDelay[x][y] = 96;
1924       break;
1925
1926     case EL_EM_DYNAMITE_ACTIVE:
1927       MovDelay[x][y] = 32;
1928       break;
1929
1930     case EL_LAMP:
1931       game.lights_still_needed++;
1932       break;
1933
1934     case EL_PENGUIN:
1935       game.friends_still_needed++;
1936       break;
1937
1938     case EL_PIG:
1939     case EL_DRAGON:
1940       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1941       break;
1942
1943     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1944     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1945     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1946     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1947     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1948     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1949     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1950     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1951     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1952     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1953     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1954     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1955       if (init_game)
1956       {
1957         int belt_nr = getBeltNrFromBeltSwitchElement(Tile[x][y]);
1958         int belt_dir = getBeltDirFromBeltSwitchElement(Tile[x][y]);
1959         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Tile[x][y]);
1960
1961         if (game.belt_dir_nr[belt_nr] == 3)     // initial value
1962         {
1963           game.belt_dir[belt_nr] = belt_dir;
1964           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1965         }
1966         else    // more than one switch -- set it like the first switch
1967         {
1968           Tile[x][y] = Tile[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1969         }
1970       }
1971       break;
1972
1973     case EL_LIGHT_SWITCH_ACTIVE:
1974       if (init_game)
1975         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1976       break;
1977
1978     case EL_INVISIBLE_STEELWALL:
1979     case EL_INVISIBLE_WALL:
1980     case EL_INVISIBLE_SAND:
1981       if (game.light_time_left > 0 ||
1982           game.lenses_time_left > 0)
1983         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
1984       break;
1985
1986     case EL_EMC_MAGIC_BALL:
1987       if (game.ball_active)
1988         Tile[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1989       break;
1990
1991     case EL_EMC_MAGIC_BALL_SWITCH:
1992       if (game.ball_active)
1993         Tile[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1994       break;
1995
1996     case EL_TRIGGER_PLAYER:
1997     case EL_TRIGGER_ELEMENT:
1998     case EL_TRIGGER_CE_VALUE:
1999     case EL_TRIGGER_CE_SCORE:
2000     case EL_SELF:
2001     case EL_ANY_ELEMENT:
2002     case EL_CURRENT_CE_VALUE:
2003     case EL_CURRENT_CE_SCORE:
2004     case EL_PREV_CE_1:
2005     case EL_PREV_CE_2:
2006     case EL_PREV_CE_3:
2007     case EL_PREV_CE_4:
2008     case EL_PREV_CE_5:
2009     case EL_PREV_CE_6:
2010     case EL_PREV_CE_7:
2011     case EL_PREV_CE_8:
2012     case EL_NEXT_CE_1:
2013     case EL_NEXT_CE_2:
2014     case EL_NEXT_CE_3:
2015     case EL_NEXT_CE_4:
2016     case EL_NEXT_CE_5:
2017     case EL_NEXT_CE_6:
2018     case EL_NEXT_CE_7:
2019     case EL_NEXT_CE_8:
2020       // reference elements should not be used on the playfield
2021       Tile[x][y] = EL_EMPTY;
2022       break;
2023
2024     default:
2025       if (IS_CUSTOM_ELEMENT(element))
2026       {
2027         if (CAN_MOVE(element))
2028           InitMovDir(x, y);
2029
2030         if (!element_info[element].use_last_ce_value || init_game)
2031           CustomValue[x][y] = GET_NEW_CE_VALUE(Tile[x][y]);
2032       }
2033       else if (IS_GROUP_ELEMENT(element))
2034       {
2035         Tile[x][y] = GetElementFromGroupElement(element);
2036
2037         InitField(x, y, init_game);
2038       }
2039       else if (IS_EMPTY_ELEMENT(element))
2040       {
2041         GfxElementEmpty[x][y] = element;
2042         Tile[x][y] = EL_EMPTY;
2043
2044         if (element_info[element].use_gfx_element)
2045           game.use_masked_elements = TRUE;
2046       }
2047
2048       break;
2049   }
2050
2051   if (!init_game)
2052     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2053 }
2054
2055 static void InitField_WithBug1(int x, int y, boolean init_game)
2056 {
2057   InitField(x, y, init_game);
2058
2059   // not needed to call InitMovDir() -- already done by InitField()!
2060   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2061       CAN_MOVE(Tile[x][y]))
2062     InitMovDir(x, y);
2063 }
2064
2065 static void InitField_WithBug2(int x, int y, boolean init_game)
2066 {
2067   int old_element = Tile[x][y];
2068
2069   InitField(x, y, init_game);
2070
2071   // not needed to call InitMovDir() -- already done by InitField()!
2072   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2073       CAN_MOVE(old_element) &&
2074       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2075     InitMovDir(x, y);
2076
2077   /* this case is in fact a combination of not less than three bugs:
2078      first, it calls InitMovDir() for elements that can move, although this is
2079      already done by InitField(); then, it checks the element that was at this
2080      field _before_ the call to InitField() (which can change it); lastly, it
2081      was not called for "mole with direction" elements, which were treated as
2082      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2083   */
2084 }
2085
2086 static int get_key_element_from_nr(int key_nr)
2087 {
2088   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2089                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2090                           EL_EM_KEY_1 : EL_KEY_1);
2091
2092   return key_base_element + key_nr;
2093 }
2094
2095 static int get_next_dropped_element(struct PlayerInfo *player)
2096 {
2097   return (player->inventory_size > 0 ?
2098           player->inventory_element[player->inventory_size - 1] :
2099           player->inventory_infinite_element != EL_UNDEFINED ?
2100           player->inventory_infinite_element :
2101           player->dynabombs_left > 0 ?
2102           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2103           EL_UNDEFINED);
2104 }
2105
2106 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2107 {
2108   // pos >= 0: get element from bottom of the stack;
2109   // pos <  0: get element from top of the stack
2110
2111   if (pos < 0)
2112   {
2113     int min_inventory_size = -pos;
2114     int inventory_pos = player->inventory_size - min_inventory_size;
2115     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2116
2117     return (player->inventory_size >= min_inventory_size ?
2118             player->inventory_element[inventory_pos] :
2119             player->inventory_infinite_element != EL_UNDEFINED ?
2120             player->inventory_infinite_element :
2121             player->dynabombs_left >= min_dynabombs_left ?
2122             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2123             EL_UNDEFINED);
2124   }
2125   else
2126   {
2127     int min_dynabombs_left = pos + 1;
2128     int min_inventory_size = pos + 1 - player->dynabombs_left;
2129     int inventory_pos = pos - player->dynabombs_left;
2130
2131     return (player->inventory_infinite_element != EL_UNDEFINED ?
2132             player->inventory_infinite_element :
2133             player->dynabombs_left >= min_dynabombs_left ?
2134             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2135             player->inventory_size >= min_inventory_size ?
2136             player->inventory_element[inventory_pos] :
2137             EL_UNDEFINED);
2138   }
2139 }
2140
2141 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2142 {
2143   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2144   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2145   int compare_result;
2146
2147   if (gpo1->sort_priority != gpo2->sort_priority)
2148     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2149   else
2150     compare_result = gpo1->nr - gpo2->nr;
2151
2152   return compare_result;
2153 }
2154
2155 int getPlayerInventorySize(int player_nr)
2156 {
2157   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2158     return game_em.ply[player_nr]->dynamite;
2159   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2160     return game_sp.red_disk_count;
2161   else
2162     return stored_player[player_nr].inventory_size;
2163 }
2164
2165 static void InitGameControlValues(void)
2166 {
2167   int i;
2168
2169   for (i = 0; game_panel_controls[i].nr != -1; i++)
2170   {
2171     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2172     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2173     struct TextPosInfo *pos = gpc->pos;
2174     int nr = gpc->nr;
2175     int type = gpc->type;
2176
2177     if (nr != i)
2178     {
2179       Error("'game_panel_controls' structure corrupted at %d", i);
2180
2181       Fail("this should not happen -- please debug");
2182     }
2183
2184     // force update of game controls after initialization
2185     gpc->value = gpc->last_value = -1;
2186     gpc->frame = gpc->last_frame = -1;
2187     gpc->gfx_frame = -1;
2188
2189     // determine panel value width for later calculation of alignment
2190     if (type == TYPE_INTEGER || type == TYPE_STRING)
2191     {
2192       pos->width = pos->size * getFontWidth(pos->font);
2193       pos->height = getFontHeight(pos->font);
2194     }
2195     else if (type == TYPE_ELEMENT)
2196     {
2197       pos->width = pos->size;
2198       pos->height = pos->size;
2199     }
2200
2201     // fill structure for game panel draw order
2202     gpo->nr = gpc->nr;
2203     gpo->sort_priority = pos->sort_priority;
2204   }
2205
2206   // sort game panel controls according to sort_priority and control number
2207   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2208         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2209 }
2210
2211 static void UpdatePlayfieldElementCount(void)
2212 {
2213   boolean use_element_count = FALSE;
2214   int i, j, x, y;
2215
2216   // first check if it is needed at all to calculate playfield element count
2217   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2218     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2219       use_element_count = TRUE;
2220
2221   if (!use_element_count)
2222     return;
2223
2224   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2225     element_info[i].element_count = 0;
2226
2227   SCAN_PLAYFIELD(x, y)
2228   {
2229     element_info[Tile[x][y]].element_count++;
2230   }
2231
2232   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2233     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2234       if (IS_IN_GROUP(j, i))
2235         element_info[EL_GROUP_START + i].element_count +=
2236           element_info[j].element_count;
2237 }
2238
2239 static void UpdateGameControlValues(void)
2240 {
2241   int i, k;
2242   int time = (game.LevelSolved ?
2243               game.LevelSolved_CountingTime :
2244               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2245               game_em.lev->time :
2246               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2247               game_sp.time_played :
2248               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2249               game_mm.energy_left :
2250               game.no_level_time_limit ? TimePlayed : TimeLeft);
2251   int score = (game.LevelSolved ?
2252                game.LevelSolved_CountingScore :
2253                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2254                game_em.lev->score :
2255                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2256                game_sp.score :
2257                level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2258                game_mm.score :
2259                game.score);
2260   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2261               game_em.lev->gems_needed :
2262               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2263               game_sp.infotrons_still_needed :
2264               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2265               game_mm.kettles_still_needed :
2266               game.gems_still_needed);
2267   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2268                      game_em.lev->gems_needed > 0 :
2269                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2270                      game_sp.infotrons_still_needed > 0 :
2271                      level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2272                      game_mm.kettles_still_needed > 0 ||
2273                      game_mm.lights_still_needed > 0 :
2274                      game.gems_still_needed > 0 ||
2275                      game.sokoban_fields_still_needed > 0 ||
2276                      game.sokoban_objects_still_needed > 0 ||
2277                      game.lights_still_needed > 0);
2278   int health = (game.LevelSolved ?
2279                 game.LevelSolved_CountingHealth :
2280                 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2281                 MM_HEALTH(game_mm.laser_overload_value) :
2282                 game.health);
2283   int sync_random_frame = INIT_GFX_RANDOM();    // random, but synchronized
2284
2285   UpdatePlayfieldElementCount();
2286
2287   // update game panel control values
2288
2289   // used instead of "level_nr" (for network games)
2290   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = levelset.level_nr;
2291   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2292
2293   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2294   for (i = 0; i < MAX_NUM_KEYS; i++)
2295     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2296   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2297   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2298
2299   if (game.centered_player_nr == -1)
2300   {
2301     for (i = 0; i < MAX_PLAYERS; i++)
2302     {
2303       // only one player in Supaplex game engine
2304       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2305         break;
2306
2307       for (k = 0; k < MAX_NUM_KEYS; k++)
2308       {
2309         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2310         {
2311           if (game_em.ply[i]->keys & (1 << k))
2312             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2313               get_key_element_from_nr(k);
2314         }
2315         else if (stored_player[i].key[k])
2316           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2317             get_key_element_from_nr(k);
2318       }
2319
2320       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2321         getPlayerInventorySize(i);
2322
2323       if (stored_player[i].num_white_keys > 0)
2324         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2325           EL_DC_KEY_WHITE;
2326
2327       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2328         stored_player[i].num_white_keys;
2329     }
2330   }
2331   else
2332   {
2333     int player_nr = game.centered_player_nr;
2334
2335     for (k = 0; k < MAX_NUM_KEYS; k++)
2336     {
2337       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2338       {
2339         if (game_em.ply[player_nr]->keys & (1 << k))
2340           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2341             get_key_element_from_nr(k);
2342       }
2343       else if (stored_player[player_nr].key[k])
2344         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2345           get_key_element_from_nr(k);
2346     }
2347
2348     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2349       getPlayerInventorySize(player_nr);
2350
2351     if (stored_player[player_nr].num_white_keys > 0)
2352       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2353
2354     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2355       stored_player[player_nr].num_white_keys;
2356   }
2357
2358   // re-arrange keys on game panel, if needed or if defined by style settings
2359   for (i = 0; i < MAX_NUM_KEYS + 1; i++)        // all normal keys + white key
2360   {
2361     int nr = GAME_PANEL_KEY_1 + i;
2362     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2363     struct TextPosInfo *pos = gpc->pos;
2364
2365     // skip check if key is not in the player's inventory
2366     if (gpc->value == EL_EMPTY)
2367       continue;
2368
2369     // check if keys should be arranged on panel from left to right
2370     if (pos->style == STYLE_LEFTMOST_POSITION)
2371     {
2372       // check previous key positions (left from current key)
2373       for (k = 0; k < i; k++)
2374       {
2375         int nr_new = GAME_PANEL_KEY_1 + k;
2376
2377         if (game_panel_controls[nr_new].value == EL_EMPTY)
2378         {
2379           game_panel_controls[nr_new].value = gpc->value;
2380           gpc->value = EL_EMPTY;
2381
2382           break;
2383         }
2384       }
2385     }
2386
2387     // check if "undefined" keys can be placed at some other position
2388     if (pos->x == -1 && pos->y == -1)
2389     {
2390       int nr_new = GAME_PANEL_KEY_1 + i % STD_NUM_KEYS;
2391
2392       // 1st try: display key at the same position as normal or EM keys
2393       if (game_panel_controls[nr_new].value == EL_EMPTY)
2394       {
2395         game_panel_controls[nr_new].value = gpc->value;
2396       }
2397       else
2398       {
2399         // 2nd try: display key at the next free position in the key panel
2400         for (k = 0; k < STD_NUM_KEYS; k++)
2401         {
2402           nr_new = GAME_PANEL_KEY_1 + k;
2403
2404           if (game_panel_controls[nr_new].value == EL_EMPTY)
2405           {
2406             game_panel_controls[nr_new].value = gpc->value;
2407
2408             break;
2409           }
2410         }
2411       }
2412     }
2413   }
2414
2415   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2416   {
2417     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2418       get_inventory_element_from_pos(local_player, i);
2419     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2420       get_inventory_element_from_pos(local_player, -i - 1);
2421   }
2422
2423   game_panel_controls[GAME_PANEL_SCORE].value = score;
2424   game_panel_controls[GAME_PANEL_HIGHSCORE].value = scores.entry[0].score;
2425
2426   game_panel_controls[GAME_PANEL_TIME].value = time;
2427
2428   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2429   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2430   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2431
2432   if (level.time == 0)
2433     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2434   else
2435     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2436
2437   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2438   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2439
2440   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2441
2442   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2443     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2444      EL_EMPTY);
2445   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2446     local_player->shield_normal_time_left;
2447   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2448     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2449      EL_EMPTY);
2450   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2451     local_player->shield_deadly_time_left;
2452
2453   game_panel_controls[GAME_PANEL_EXIT].value =
2454     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2455
2456   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2457     (game.ball_active ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2458   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2459     (game.ball_active ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2460      EL_EMC_MAGIC_BALL_SWITCH);
2461
2462   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2463     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2464   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2465     game.light_time_left;
2466
2467   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2468     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2469   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2470     game.timegate_time_left;
2471
2472   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2473     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2474
2475   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2476     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2477   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2478     game.lenses_time_left;
2479
2480   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2481     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2482   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2483     game.magnify_time_left;
2484
2485   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2486     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2487      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2488      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2489      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2490      EL_BALLOON_SWITCH_NONE);
2491
2492   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2493     local_player->dynabomb_count;
2494   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2495     local_player->dynabomb_size;
2496   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2497     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2498
2499   game_panel_controls[GAME_PANEL_PENGUINS].value =
2500     game.friends_still_needed;
2501
2502   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2503     game.sokoban_objects_still_needed;
2504   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2505     game.sokoban_fields_still_needed;
2506
2507   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2508     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2509
2510   for (i = 0; i < NUM_BELTS; i++)
2511   {
2512     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2513       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2514        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2515     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2516       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2517   }
2518
2519   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2520     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2521   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2522     game.magic_wall_time_left;
2523
2524   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2525     local_player->gravity;
2526
2527   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2528     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2529
2530   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2531     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2532       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2533        game.panel.element[i].id : EL_UNDEFINED);
2534
2535   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2536     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2537       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2538        element_info[game.panel.element_count[i].id].element_count : 0);
2539
2540   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2541     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2542       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2543        element_info[game.panel.ce_score[i].id].collect_score : 0);
2544
2545   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2546     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2547       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2548        element_info[game.panel.ce_score_element[i].id].collect_score :
2549        EL_UNDEFINED);
2550
2551   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2552   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2553   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2554
2555   // update game panel control frames
2556
2557   for (i = 0; game_panel_controls[i].nr != -1; i++)
2558   {
2559     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2560
2561     if (gpc->type == TYPE_ELEMENT)
2562     {
2563       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2564       {
2565         int last_anim_random_frame = gfx.anim_random_frame;
2566         int element = gpc->value;
2567         int graphic = el2panelimg(element);
2568         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2569                                sync_random_frame :
2570                                graphic_info[graphic].anim_global_anim_sync ?
2571                                getGlobalAnimSyncFrame() : INIT_GFX_RANDOM());
2572
2573         if (gpc->value != gpc->last_value)
2574         {
2575           gpc->gfx_frame = 0;
2576           gpc->gfx_random = init_gfx_random;
2577         }
2578         else
2579         {
2580           gpc->gfx_frame++;
2581
2582           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2583               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2584             gpc->gfx_random = init_gfx_random;
2585         }
2586
2587         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2588           gfx.anim_random_frame = gpc->gfx_random;
2589
2590         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2591           gpc->gfx_frame = element_info[element].collect_score;
2592
2593         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2594
2595         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2596           gfx.anim_random_frame = last_anim_random_frame;
2597       }
2598     }
2599     else if (gpc->type == TYPE_GRAPHIC)
2600     {
2601       if (gpc->graphic != IMG_UNDEFINED)
2602       {
2603         int last_anim_random_frame = gfx.anim_random_frame;
2604         int graphic = gpc->graphic;
2605         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2606                                sync_random_frame :
2607                                graphic_info[graphic].anim_global_anim_sync ?
2608                                getGlobalAnimSyncFrame() : INIT_GFX_RANDOM());
2609
2610         if (gpc->value != gpc->last_value)
2611         {
2612           gpc->gfx_frame = 0;
2613           gpc->gfx_random = init_gfx_random;
2614         }
2615         else
2616         {
2617           gpc->gfx_frame++;
2618
2619           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2620               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2621             gpc->gfx_random = init_gfx_random;
2622         }
2623
2624         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2625           gfx.anim_random_frame = gpc->gfx_random;
2626
2627         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2628
2629         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2630           gfx.anim_random_frame = last_anim_random_frame;
2631       }
2632     }
2633   }
2634 }
2635
2636 static void DisplayGameControlValues(void)
2637 {
2638   boolean redraw_panel = FALSE;
2639   int i;
2640
2641   for (i = 0; game_panel_controls[i].nr != -1; i++)
2642   {
2643     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2644
2645     if (PANEL_DEACTIVATED(gpc->pos))
2646       continue;
2647
2648     if (gpc->value == gpc->last_value &&
2649         gpc->frame == gpc->last_frame)
2650       continue;
2651
2652     redraw_panel = TRUE;
2653   }
2654
2655   if (!redraw_panel)
2656     return;
2657
2658   // copy default game door content to main double buffer
2659
2660   // !!! CHECK AGAIN !!!
2661   SetPanelBackground();
2662   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2663   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2664
2665   // redraw game control buttons
2666   RedrawGameButtons();
2667
2668   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2669
2670   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2671   {
2672     int nr = game_panel_order[i].nr;
2673     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2674     struct TextPosInfo *pos = gpc->pos;
2675     int type = gpc->type;
2676     int value = gpc->value;
2677     int frame = gpc->frame;
2678     int size = pos->size;
2679     int font = pos->font;
2680     boolean draw_masked = pos->draw_masked;
2681     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2682
2683     if (PANEL_DEACTIVATED(pos))
2684       continue;
2685
2686     if (pos->class == get_hash_from_key("extra_panel_items") &&
2687         !setup.prefer_extra_panel_items)
2688       continue;
2689
2690     gpc->last_value = value;
2691     gpc->last_frame = frame;
2692
2693     if (type == TYPE_INTEGER)
2694     {
2695       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2696           nr == GAME_PANEL_INVENTORY_COUNT ||
2697           nr == GAME_PANEL_SCORE ||
2698           nr == GAME_PANEL_HIGHSCORE ||
2699           nr == GAME_PANEL_TIME)
2700       {
2701         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2702
2703         if (use_dynamic_size)           // use dynamic number of digits
2704         {
2705           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 :
2706                               nr == GAME_PANEL_INVENTORY_COUNT ||
2707                               nr == GAME_PANEL_TIME ? 1000 : 100000);
2708           int size_add = (nr == GAME_PANEL_LEVEL_NUMBER ||
2709                           nr == GAME_PANEL_INVENTORY_COUNT ||
2710                           nr == GAME_PANEL_TIME ? 1 : 2);
2711           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 :
2712                        nr == GAME_PANEL_INVENTORY_COUNT ||
2713                        nr == GAME_PANEL_TIME ? 3 : 5);
2714           int size2 = size1 + size_add;
2715           int font1 = pos->font;
2716           int font2 = pos->font_alt;
2717
2718           size = (value < value_change ? size1 : size2);
2719           font = (value < value_change ? font1 : font2);
2720         }
2721       }
2722
2723       // correct text size if "digits" is zero or less
2724       if (size <= 0)
2725         size = strlen(int2str(value, size));
2726
2727       // dynamically correct text alignment
2728       pos->width = size * getFontWidth(font);
2729
2730       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2731                   int2str(value, size), font, mask_mode);
2732     }
2733     else if (type == TYPE_ELEMENT)
2734     {
2735       int element, graphic;
2736       Bitmap *src_bitmap;
2737       int src_x, src_y;
2738       int width, height;
2739       int dst_x = PANEL_XPOS(pos);
2740       int dst_y = PANEL_YPOS(pos);
2741
2742       if (value != EL_UNDEFINED && value != EL_EMPTY)
2743       {
2744         element = value;
2745         graphic = el2panelimg(value);
2746
2747 #if 0
2748         Debug("game:DisplayGameControlValues", "%d, '%s' [%d]",
2749               element, EL_NAME(element), size);
2750 #endif
2751
2752         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2753           size = TILESIZE;
2754
2755         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2756                               &src_x, &src_y);
2757
2758         width  = graphic_info[graphic].width  * size / TILESIZE;
2759         height = graphic_info[graphic].height * size / TILESIZE;
2760
2761         if (draw_masked)
2762           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2763                            dst_x, dst_y);
2764         else
2765           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2766                      dst_x, dst_y);
2767       }
2768     }
2769     else if (type == TYPE_GRAPHIC)
2770     {
2771       int graphic        = gpc->graphic;
2772       int graphic_active = gpc->graphic_active;
2773       Bitmap *src_bitmap;
2774       int src_x, src_y;
2775       int width, height;
2776       int dst_x = PANEL_XPOS(pos);
2777       int dst_y = PANEL_YPOS(pos);
2778       boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2779                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2780
2781       if (graphic != IMG_UNDEFINED && !skip)
2782       {
2783         if (pos->style == STYLE_REVERSE)
2784           value = 100 - value;
2785
2786         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2787
2788         if (pos->direction & MV_HORIZONTAL)
2789         {
2790           width  = graphic_info[graphic_active].width * value / 100;
2791           height = graphic_info[graphic_active].height;
2792
2793           if (pos->direction == MV_LEFT)
2794           {
2795             src_x += graphic_info[graphic_active].width - width;
2796             dst_x += graphic_info[graphic_active].width - width;
2797           }
2798         }
2799         else
2800         {
2801           width  = graphic_info[graphic_active].width;
2802           height = graphic_info[graphic_active].height * value / 100;
2803
2804           if (pos->direction == MV_UP)
2805           {
2806             src_y += graphic_info[graphic_active].height - height;
2807             dst_y += graphic_info[graphic_active].height - height;
2808           }
2809         }
2810
2811         if (draw_masked)
2812           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2813                            dst_x, dst_y);
2814         else
2815           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2816                      dst_x, dst_y);
2817
2818         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2819
2820         if (pos->direction & MV_HORIZONTAL)
2821         {
2822           if (pos->direction == MV_RIGHT)
2823           {
2824             src_x += width;
2825             dst_x += width;
2826           }
2827           else
2828           {
2829             dst_x = PANEL_XPOS(pos);
2830           }
2831
2832           width = graphic_info[graphic].width - width;
2833         }
2834         else
2835         {
2836           if (pos->direction == MV_DOWN)
2837           {
2838             src_y += height;
2839             dst_y += height;
2840           }
2841           else
2842           {
2843             dst_y = PANEL_YPOS(pos);
2844           }
2845
2846           height = graphic_info[graphic].height - height;
2847         }
2848
2849         if (draw_masked)
2850           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2851                            dst_x, dst_y);
2852         else
2853           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2854                      dst_x, dst_y);
2855       }
2856     }
2857     else if (type == TYPE_STRING)
2858     {
2859       boolean active = (value != 0);
2860       char *state_normal = "off";
2861       char *state_active = "on";
2862       char *state = (active ? state_active : state_normal);
2863       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2864                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2865                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2866                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2867
2868       if (nr == GAME_PANEL_GRAVITY_STATE)
2869       {
2870         int font1 = pos->font;          // (used for normal state)
2871         int font2 = pos->font_alt;      // (used for active state)
2872
2873         font = (active ? font2 : font1);
2874       }
2875
2876       if (s != NULL)
2877       {
2878         char *s_cut;
2879
2880         if (size <= 0)
2881         {
2882           // don't truncate output if "chars" is zero or less
2883           size = strlen(s);
2884
2885           // dynamically correct text alignment
2886           pos->width = size * getFontWidth(font);
2887         }
2888
2889         s_cut = getStringCopyN(s, size);
2890
2891         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2892                     s_cut, font, mask_mode);
2893
2894         free(s_cut);
2895       }
2896     }
2897
2898     redraw_mask |= REDRAW_DOOR_1;
2899   }
2900
2901   SetGameStatus(GAME_MODE_PLAYING);
2902 }
2903
2904 void UpdateAndDisplayGameControlValues(void)
2905 {
2906   if (tape.deactivate_display)
2907     return;
2908
2909   UpdateGameControlValues();
2910   DisplayGameControlValues();
2911 }
2912
2913 void UpdateGameDoorValues(void)
2914 {
2915   UpdateGameControlValues();
2916 }
2917
2918 void DrawGameDoorValues(void)
2919 {
2920   DisplayGameControlValues();
2921 }
2922
2923
2924 // ============================================================================
2925 // InitGameEngine()
2926 // ----------------------------------------------------------------------------
2927 // initialize game engine due to level / tape version number
2928 // ============================================================================
2929
2930 static void InitGameEngine(void)
2931 {
2932   int i, j, k, l, x, y;
2933
2934   // set game engine from tape file when re-playing, else from level file
2935   game.engine_version = (tape.playing ? tape.engine_version :
2936                          level.game_version);
2937
2938   // set single or multi-player game mode (needed for re-playing tapes)
2939   game.team_mode = setup.team_mode;
2940
2941   if (tape.playing)
2942   {
2943     int num_players = 0;
2944
2945     for (i = 0; i < MAX_PLAYERS; i++)
2946       if (tape.player_participates[i])
2947         num_players++;
2948
2949     // multi-player tapes contain input data for more than one player
2950     game.team_mode = (num_players > 1);
2951   }
2952
2953 #if 0
2954   Debug("game:init:level", "level %d: level.game_version  == %06d", level_nr,
2955         level.game_version);
2956   Debug("game:init:level", "          tape.file_version   == %06d",
2957         tape.file_version);
2958   Debug("game:init:level", "          tape.game_version   == %06d",
2959         tape.game_version);
2960   Debug("game:init:level", "          tape.engine_version == %06d",
2961         tape.engine_version);
2962   Debug("game:init:level", "       => game.engine_version == %06d [tape mode: %s]",
2963         game.engine_version, (tape.playing ? "PLAYING" : "RECORDING"));
2964 #endif
2965
2966   // --------------------------------------------------------------------------
2967   // set flags for bugs and changes according to active game engine version
2968   // --------------------------------------------------------------------------
2969
2970   /*
2971     Summary of bugfix:
2972     Fixed property "can fall" for run-time element "EL_AMOEBA_DROPPING"
2973
2974     Bug was introduced in version:
2975     2.0.1
2976
2977     Bug was fixed in version:
2978     4.2.0.0
2979
2980     Description:
2981     In version 2.0.1, a new run-time element "EL_AMOEBA_DROPPING" was added,
2982     but the property "can fall" was missing, which caused some levels to be
2983     unsolvable. This was fixed in version 4.2.0.0.
2984
2985     Affected levels/tapes:
2986     An example for a tape that was fixed by this bugfix is tape 029 from the
2987     level set "rnd_sam_bateman".
2988     The wrong behaviour will still be used for all levels or tapes that were
2989     created/recorded with it. An example for this is tape 023 from the level
2990     set "rnd_gerhard_haeusler", which was recorded with a buggy game engine.
2991   */
2992
2993   boolean use_amoeba_dropping_cannot_fall_bug =
2994     ((game.engine_version >= VERSION_IDENT(2,0,1,0) &&
2995       game.engine_version <  VERSION_IDENT(4,2,0,0)) ||
2996      (tape.playing &&
2997       tape.game_version >= VERSION_IDENT(2,0,1,0) &&
2998       tape.game_version <  VERSION_IDENT(4,2,0,0)));
2999
3000   /*
3001     Summary of bugfix/change:
3002     Fixed move speed of elements entering or leaving magic wall.
3003
3004     Fixed/changed in version:
3005     2.0.1
3006
3007     Description:
3008     Before 2.0.1, move speed of elements entering or leaving magic wall was
3009     twice as fast as it is now.
3010     Since 2.0.1, this is set to a lower value by using move_stepsize_list[].
3011
3012     Affected levels/tapes:
3013     The first condition is generally needed for all levels/tapes before version
3014     2.0.1, which might use the old behaviour before it was changed; known tapes
3015     that are affected: Tape 014 from the level set "rnd_conor_mancone".
3016     The second condition is an exception from the above case and is needed for
3017     the special case of tapes recorded with game (not engine!) version 2.0.1 or
3018     above, but before it was known that this change would break tapes like the
3019     above and was fixed in 4.2.0.0, so that the changed behaviour was active
3020     although the engine version while recording maybe was before 2.0.1. There
3021     are a lot of tapes that are affected by this exception, like tape 006 from
3022     the level set "rnd_conor_mancone".
3023   */
3024
3025   boolean use_old_move_stepsize_for_magic_wall =
3026     (game.engine_version < VERSION_IDENT(2,0,1,0) &&
3027      !(tape.playing &&
3028        tape.game_version >= VERSION_IDENT(2,0,1,0) &&
3029        tape.game_version <  VERSION_IDENT(4,2,0,0)));
3030
3031   /*
3032     Summary of bugfix/change:
3033     Fixed handling for custom elements that change when pushed by the player.
3034
3035     Fixed/changed in version:
3036     3.1.0
3037
3038     Description:
3039     Before 3.1.0, custom elements that "change when pushing" changed directly
3040     after the player started pushing them (until then handled in "DigField()").
3041     Since 3.1.0, these custom elements are not changed until the "pushing"
3042     move of the element is finished (now handled in "ContinueMoving()").
3043
3044     Affected levels/tapes:
3045     The first condition is generally needed for all levels/tapes before version
3046     3.1.0, which might use the old behaviour before it was changed; known tapes
3047     that are affected are some tapes from the level set "Walpurgis Gardens" by
3048     Jamie Cullen.
3049     The second condition is an exception from the above case and is needed for
3050     the special case of tapes recorded with game (not engine!) version 3.1.0 or
3051     above (including some development versions of 3.1.0), but before it was
3052     known that this change would break tapes like the above and was fixed in
3053     3.1.1, so that the changed behaviour was active although the engine version
3054     while recording maybe was before 3.1.0. There is at least one tape that is
3055     affected by this exception, which is the tape for the one-level set "Bug
3056     Machine" by Juergen Bonhagen.
3057   */
3058
3059   game.use_change_when_pushing_bug =
3060     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3061      !(tape.playing &&
3062        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3063        tape.game_version <  VERSION_IDENT(3,1,1,0)));
3064
3065   /*
3066     Summary of bugfix/change:
3067     Fixed handling for blocking the field the player leaves when moving.
3068
3069     Fixed/changed in version:
3070     3.1.1
3071
3072     Description:
3073     Before 3.1.1, when "block last field when moving" was enabled, the field
3074     the player is leaving when moving was blocked for the time of the move,
3075     and was directly unblocked afterwards. This resulted in the last field
3076     being blocked for exactly one less than the number of frames of one player
3077     move. Additionally, even when blocking was disabled, the last field was
3078     blocked for exactly one frame.
3079     Since 3.1.1, due to changes in player movement handling, the last field
3080     is not blocked at all when blocking is disabled. When blocking is enabled,
3081     the last field is blocked for exactly the number of frames of one player
3082     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3083     last field is blocked for exactly one more than the number of frames of
3084     one player move.
3085
3086     Affected levels/tapes:
3087     (!!! yet to be determined -- probably many !!!)
3088   */
3089
3090   game.use_block_last_field_bug =
3091     (game.engine_version < VERSION_IDENT(3,1,1,0));
3092
3093   /* various special flags and settings for native Emerald Mine game engine */
3094
3095   game_em.use_single_button =
3096     (game.engine_version > VERSION_IDENT(4,0,0,2));
3097
3098   game_em.use_push_delay =
3099     (game.engine_version > VERSION_IDENT(4,3,7,1));
3100
3101   game_em.use_snap_key_bug =
3102     (game.engine_version < VERSION_IDENT(4,0,1,0));
3103
3104   game_em.use_random_bug =
3105     (tape.property_bits & TAPE_PROPERTY_EM_RANDOM_BUG);
3106
3107   boolean use_old_em_engine = (game.engine_version < VERSION_IDENT(4,2,0,0));
3108
3109   game_em.use_old_explosions            = use_old_em_engine;
3110   game_em.use_old_android               = use_old_em_engine;
3111   game_em.use_old_push_elements         = use_old_em_engine;
3112   game_em.use_old_push_into_acid        = use_old_em_engine;
3113
3114   game_em.use_wrap_around               = !use_old_em_engine;
3115
3116   // --------------------------------------------------------------------------
3117
3118   // set maximal allowed number of custom element changes per game frame
3119   game.max_num_changes_per_frame = 1;
3120
3121   // default scan direction: scan playfield from top/left to bottom/right
3122   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3123
3124   // dynamically adjust element properties according to game engine version
3125   InitElementPropertiesEngine(game.engine_version);
3126
3127   // ---------- initialize special element properties -------------------------
3128
3129   // "EL_AMOEBA_DROPPING" missed property "can fall" in older game versions
3130   if (use_amoeba_dropping_cannot_fall_bug)
3131     SET_PROPERTY(EL_AMOEBA_DROPPING, EP_CAN_FALL, FALSE);
3132
3133   // ---------- initialize player's initial move delay ------------------------
3134
3135   // dynamically adjust player properties according to level information
3136   for (i = 0; i < MAX_PLAYERS; i++)
3137     game.initial_move_delay_value[i] =
3138       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3139
3140   // dynamically adjust player properties according to game engine version
3141   for (i = 0; i < MAX_PLAYERS; i++)
3142     game.initial_move_delay[i] =
3143       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3144        game.initial_move_delay_value[i] : 0);
3145
3146   // ---------- initialize player's initial push delay ------------------------
3147
3148   // dynamically adjust player properties according to game engine version
3149   game.initial_push_delay_value =
3150     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3151
3152   // ---------- initialize changing elements ----------------------------------
3153
3154   // initialize changing elements information
3155   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3156   {
3157     struct ElementInfo *ei = &element_info[i];
3158
3159     // this pointer might have been changed in the level editor
3160     ei->change = &ei->change_page[0];
3161
3162     if (!IS_CUSTOM_ELEMENT(i))
3163     {
3164       ei->change->target_element = EL_EMPTY_SPACE;
3165       ei->change->delay_fixed = 0;
3166       ei->change->delay_random = 0;
3167       ei->change->delay_frames = 1;
3168     }
3169
3170     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3171     {
3172       ei->has_change_event[j] = FALSE;
3173
3174       ei->event_page_nr[j] = 0;
3175       ei->event_page[j] = &ei->change_page[0];
3176     }
3177   }
3178
3179   // add changing elements from pre-defined list
3180   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3181   {
3182     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3183     struct ElementInfo *ei = &element_info[ch_delay->element];
3184
3185     ei->change->target_element       = ch_delay->target_element;
3186     ei->change->delay_fixed          = ch_delay->change_delay;
3187
3188     ei->change->pre_change_function  = ch_delay->pre_change_function;
3189     ei->change->change_function      = ch_delay->change_function;
3190     ei->change->post_change_function = ch_delay->post_change_function;
3191
3192     ei->change->can_change = TRUE;
3193     ei->change->can_change_or_has_action = TRUE;
3194
3195     ei->has_change_event[CE_DELAY] = TRUE;
3196
3197     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3198     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3199   }
3200
3201   // ---------- initialize if element can trigger global animations -----------
3202
3203   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3204   {
3205     struct ElementInfo *ei = &element_info[i];
3206
3207     ei->has_anim_event = FALSE;
3208   }
3209
3210   InitGlobalAnimEventsForCustomElements();
3211
3212   // ---------- initialize internal run-time variables ------------------------
3213
3214   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3215   {
3216     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3217
3218     for (j = 0; j < ei->num_change_pages; j++)
3219     {
3220       ei->change_page[j].can_change_or_has_action =
3221         (ei->change_page[j].can_change |
3222          ei->change_page[j].has_action);
3223     }
3224   }
3225
3226   // add change events from custom element configuration
3227   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3228   {
3229     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3230
3231     for (j = 0; j < ei->num_change_pages; j++)
3232     {
3233       if (!ei->change_page[j].can_change_or_has_action)
3234         continue;
3235
3236       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3237       {
3238         // only add event page for the first page found with this event
3239         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3240         {
3241           ei->has_change_event[k] = TRUE;
3242
3243           ei->event_page_nr[k] = j;
3244           ei->event_page[k] = &ei->change_page[j];
3245         }
3246       }
3247     }
3248   }
3249
3250   // ---------- initialize reference elements in change conditions ------------
3251
3252   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3253   {
3254     int element = EL_CUSTOM_START + i;
3255     struct ElementInfo *ei = &element_info[element];
3256
3257     for (j = 0; j < ei->num_change_pages; j++)
3258     {
3259       int trigger_element = ei->change_page[j].initial_trigger_element;
3260
3261       if (trigger_element >= EL_PREV_CE_8 &&
3262           trigger_element <= EL_NEXT_CE_8)
3263         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3264
3265       ei->change_page[j].trigger_element = trigger_element;
3266     }
3267   }
3268
3269   // ---------- initialize run-time trigger player and element ----------------
3270
3271   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3272   {
3273     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3274
3275     for (j = 0; j < ei->num_change_pages; j++)
3276     {
3277       struct ElementChangeInfo *change = &ei->change_page[j];
3278
3279       change->actual_trigger_element = EL_EMPTY;
3280       change->actual_trigger_player = EL_EMPTY;
3281       change->actual_trigger_player_bits = CH_PLAYER_NONE;
3282       change->actual_trigger_side = CH_SIDE_NONE;
3283       change->actual_trigger_ce_value = 0;
3284       change->actual_trigger_ce_score = 0;
3285       change->actual_trigger_x = -1;
3286       change->actual_trigger_y = -1;
3287     }
3288   }
3289
3290   // ---------- initialize trigger events -------------------------------------
3291
3292   // initialize trigger events information
3293   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3294     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3295       trigger_events[i][j] = FALSE;
3296
3297   // add trigger events from element change event properties
3298   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3299   {
3300     struct ElementInfo *ei = &element_info[i];
3301
3302     for (j = 0; j < ei->num_change_pages; j++)
3303     {
3304       struct ElementChangeInfo *change = &ei->change_page[j];
3305
3306       if (!change->can_change_or_has_action)
3307         continue;
3308
3309       if (change->has_event[CE_BY_OTHER_ACTION])
3310       {
3311         int trigger_element = change->trigger_element;
3312
3313         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3314         {
3315           if (change->has_event[k])
3316           {
3317             if (IS_GROUP_ELEMENT(trigger_element))
3318             {
3319               struct ElementGroupInfo *group =
3320                 element_info[trigger_element].group;
3321
3322               for (l = 0; l < group->num_elements_resolved; l++)
3323                 trigger_events[group->element_resolved[l]][k] = TRUE;
3324             }
3325             else if (trigger_element == EL_ANY_ELEMENT)
3326               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3327                 trigger_events[l][k] = TRUE;
3328             else
3329               trigger_events[trigger_element][k] = TRUE;
3330           }
3331         }
3332       }
3333     }
3334   }
3335
3336   // ---------- initialize push delay -----------------------------------------
3337
3338   // initialize push delay values to default
3339   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3340   {
3341     if (!IS_CUSTOM_ELEMENT(i))
3342     {
3343       // set default push delay values (corrected since version 3.0.7-1)
3344       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3345       {
3346         element_info[i].push_delay_fixed = 2;
3347         element_info[i].push_delay_random = 8;
3348       }
3349       else
3350       {
3351         element_info[i].push_delay_fixed = 8;
3352         element_info[i].push_delay_random = 8;
3353       }
3354     }
3355   }
3356
3357   // set push delay value for certain elements from pre-defined list
3358   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3359   {
3360     int e = push_delay_list[i].element;
3361
3362     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3363     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3364   }
3365
3366   // set push delay value for Supaplex elements for newer engine versions
3367   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3368   {
3369     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3370     {
3371       if (IS_SP_ELEMENT(i))
3372       {
3373         // set SP push delay to just enough to push under a falling zonk
3374         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3375
3376         element_info[i].push_delay_fixed  = delay;
3377         element_info[i].push_delay_random = 0;
3378       }
3379     }
3380   }
3381
3382   // ---------- initialize move stepsize --------------------------------------
3383
3384   // initialize move stepsize values to default
3385   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3386     if (!IS_CUSTOM_ELEMENT(i))
3387       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3388
3389   // set move stepsize value for certain elements from pre-defined list
3390   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3391   {
3392     int e = move_stepsize_list[i].element;
3393
3394     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3395
3396     // set move stepsize value for certain elements for older engine versions
3397     if (use_old_move_stepsize_for_magic_wall)
3398     {
3399       if (e == EL_MAGIC_WALL_FILLING ||
3400           e == EL_MAGIC_WALL_EMPTYING ||
3401           e == EL_BD_MAGIC_WALL_FILLING ||
3402           e == EL_BD_MAGIC_WALL_EMPTYING)
3403         element_info[e].move_stepsize *= 2;
3404     }
3405   }
3406
3407   // ---------- initialize collect score --------------------------------------
3408
3409   // initialize collect score values for custom elements from initial value
3410   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3411     if (IS_CUSTOM_ELEMENT(i))
3412       element_info[i].collect_score = element_info[i].collect_score_initial;
3413
3414   // ---------- initialize collect count --------------------------------------
3415
3416   // initialize collect count values for non-custom elements
3417   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3418     if (!IS_CUSTOM_ELEMENT(i))
3419       element_info[i].collect_count_initial = 0;
3420
3421   // add collect count values for all elements from pre-defined list
3422   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3423     element_info[collect_count_list[i].element].collect_count_initial =
3424       collect_count_list[i].count;
3425
3426   // ---------- initialize access direction -----------------------------------
3427
3428   // initialize access direction values to default (access from every side)
3429   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3430     if (!IS_CUSTOM_ELEMENT(i))
3431       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3432
3433   // set access direction value for certain elements from pre-defined list
3434   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3435     element_info[access_direction_list[i].element].access_direction =
3436       access_direction_list[i].direction;
3437
3438   // ---------- initialize explosion content ----------------------------------
3439   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3440   {
3441     if (IS_CUSTOM_ELEMENT(i))
3442       continue;
3443
3444     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3445     {
3446       // (content for EL_YAMYAM set at run-time with game.yamyam_content_nr)
3447
3448       element_info[i].content.e[x][y] =
3449         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3450          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3451          i == EL_PLAYER_3 ? EL_EMERALD :
3452          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3453          i == EL_MOLE ? EL_EMERALD_RED :
3454          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3455          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3456          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3457          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3458          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3459          i == EL_WALL_EMERALD ? EL_EMERALD :
3460          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3461          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3462          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3463          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3464          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3465          i == EL_WALL_PEARL ? EL_PEARL :
3466          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3467          EL_EMPTY);
3468     }
3469   }
3470
3471   // ---------- initialize recursion detection --------------------------------
3472   recursion_loop_depth = 0;
3473   recursion_loop_detected = FALSE;
3474   recursion_loop_element = EL_UNDEFINED;
3475
3476   // ---------- initialize graphics engine ------------------------------------
3477   game.scroll_delay_value =
3478     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3479      level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3480      !setup.forced_scroll_delay           ? 0 :
3481      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3482   if (game.forced_scroll_delay_value == -1)
3483     game.scroll_delay_value =
3484       MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3485
3486   // ---------- initialize game engine snapshots ------------------------------
3487   for (i = 0; i < MAX_PLAYERS; i++)
3488     game.snapshot.last_action[i] = 0;
3489   game.snapshot.changed_action = FALSE;
3490   game.snapshot.collected_item = FALSE;
3491   game.snapshot.mode =
3492     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3493      SNAPSHOT_MODE_EVERY_STEP :
3494      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3495      SNAPSHOT_MODE_EVERY_MOVE :
3496      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3497      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3498   game.snapshot.save_snapshot = FALSE;
3499
3500   // ---------- initialize level time for Supaplex engine ---------------------
3501   // Supaplex levels with time limit currently unsupported -- should be added
3502   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3503     level.time = 0;
3504
3505   // ---------- initialize flags for handling game actions --------------------
3506
3507   // set flags for game actions to default values
3508   game.use_key_actions = TRUE;
3509   game.use_mouse_actions = FALSE;
3510
3511   // when using Mirror Magic game engine, handle mouse events only
3512   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
3513   {
3514     game.use_key_actions = FALSE;
3515     game.use_mouse_actions = TRUE;
3516   }
3517
3518   // check for custom elements with mouse click events
3519   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3520   {
3521     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3522     {
3523       int element = EL_CUSTOM_START + i;
3524
3525       if (HAS_ANY_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
3526           HAS_ANY_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE) ||
3527           HAS_ANY_CHANGE_EVENT(element, CE_MOUSE_CLICKED_ON_X) ||
3528           HAS_ANY_CHANGE_EVENT(element, CE_MOUSE_PRESSED_ON_X))
3529         game.use_mouse_actions = TRUE;
3530     }
3531   }
3532 }
3533
3534 static int get_num_special_action(int element, int action_first,
3535                                   int action_last)
3536 {
3537   int num_special_action = 0;
3538   int i, j;
3539
3540   for (i = action_first; i <= action_last; i++)
3541   {
3542     boolean found = FALSE;
3543
3544     for (j = 0; j < NUM_DIRECTIONS; j++)
3545       if (el_act_dir2img(element, i, j) !=
3546           el_act_dir2img(element, ACTION_DEFAULT, j))
3547         found = TRUE;
3548
3549     if (found)
3550       num_special_action++;
3551     else
3552       break;
3553   }
3554
3555   return num_special_action;
3556 }
3557
3558
3559 // ============================================================================
3560 // InitGame()
3561 // ----------------------------------------------------------------------------
3562 // initialize and start new game
3563 // ============================================================================
3564
3565 #if DEBUG_INIT_PLAYER
3566 static void DebugPrintPlayerStatus(char *message)
3567 {
3568   int i;
3569
3570   if (!options.debug)
3571     return;
3572
3573   Debug("game:init:player", "%s:", message);
3574
3575   for (i = 0; i < MAX_PLAYERS; i++)
3576   {
3577     struct PlayerInfo *player = &stored_player[i];
3578
3579     Debug("game:init:player",
3580           "- player %d: present == %d, connected == %d [%d/%d], active == %d%s",
3581           i + 1,
3582           player->present,
3583           player->connected,
3584           player->connected_locally,
3585           player->connected_network,
3586           player->active,
3587           (local_player == player ? " (local player)" : ""));
3588   }
3589 }
3590 #endif
3591
3592 void InitGame(void)
3593 {
3594   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3595   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3596   int fade_mask = REDRAW_FIELD;
3597   boolean restarting = (game_status == GAME_MODE_PLAYING);
3598   boolean emulate_bd = TRUE;    // unless non-BOULDERDASH elements found
3599   boolean emulate_sp = TRUE;    // unless non-SUPAPLEX    elements found
3600   int initial_move_dir = MV_DOWN;
3601   int i, j, x, y;
3602
3603   // required here to update video display before fading (FIX THIS)
3604   DrawMaskedBorder(REDRAW_DOOR_2);
3605
3606   if (!game.restart_level)
3607     CloseDoor(DOOR_CLOSE_1);
3608
3609   if (restarting)
3610   {
3611     // force fading out global animations displayed during game play
3612     SetGameStatus(GAME_MODE_PSEUDO_RESTARTING);
3613   }
3614   else
3615   {
3616     SetGameStatus(GAME_MODE_PLAYING);
3617   }
3618
3619   if (level_editor_test_game)
3620     FadeSkipNextFadeOut();
3621   else
3622     FadeSetEnterScreen();
3623
3624   if (CheckFadeAll())
3625     fade_mask = REDRAW_ALL;
3626
3627   FadeLevelSoundsAndMusic();
3628
3629   ExpireSoundLoops(TRUE);
3630
3631   FadeOut(fade_mask);
3632
3633   if (restarting)
3634   {
3635     // force restarting global animations displayed during game play
3636     RestartGlobalAnimsByStatus(GAME_MODE_PSEUDO_RESTARTING);
3637
3638     // this is required for "transforming" fade modes like cross-fading
3639     // (else global animations will be stopped, but not restarted here)
3640     SetAnimStatusBeforeFading(GAME_MODE_PSEUDO_RESTARTING);
3641
3642     SetGameStatus(GAME_MODE_PLAYING);
3643   }
3644
3645   if (level_editor_test_game)
3646     FadeSkipNextFadeIn();
3647
3648   // needed if different viewport properties defined for playing
3649   ChangeViewportPropertiesIfNeeded();
3650
3651   ClearField();
3652
3653   DrawCompleteVideoDisplay();
3654
3655   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3656
3657   InitGameEngine();
3658   InitGameControlValues();
3659
3660   if (tape.recording)
3661   {
3662     // initialize tape actions from game when recording tape
3663     tape.use_key_actions   = game.use_key_actions;
3664     tape.use_mouse_actions = game.use_mouse_actions;
3665
3666     // initialize visible playfield size when recording tape (for team mode)
3667     tape.scr_fieldx = SCR_FIELDX;
3668     tape.scr_fieldy = SCR_FIELDY;
3669   }
3670
3671   // don't play tapes over network
3672   network_playing = (network.enabled && !tape.playing);
3673
3674   for (i = 0; i < MAX_PLAYERS; i++)
3675   {
3676     struct PlayerInfo *player = &stored_player[i];
3677
3678     player->index_nr = i;
3679     player->index_bit = (1 << i);
3680     player->element_nr = EL_PLAYER_1 + i;
3681
3682     player->present = FALSE;
3683     player->active = FALSE;
3684     player->mapped = FALSE;
3685
3686     player->killed = FALSE;
3687     player->reanimated = FALSE;
3688     player->buried = FALSE;
3689
3690     player->action = 0;
3691     player->effective_action = 0;
3692     player->programmed_action = 0;
3693     player->snap_action = 0;
3694
3695     player->mouse_action.lx = 0;
3696     player->mouse_action.ly = 0;
3697     player->mouse_action.button = 0;
3698     player->mouse_action.button_hint = 0;
3699
3700     player->effective_mouse_action.lx = 0;
3701     player->effective_mouse_action.ly = 0;
3702     player->effective_mouse_action.button = 0;
3703     player->effective_mouse_action.button_hint = 0;
3704
3705     for (j = 0; j < MAX_NUM_KEYS; j++)
3706       player->key[j] = FALSE;
3707
3708     player->num_white_keys = 0;
3709
3710     player->dynabomb_count = 0;
3711     player->dynabomb_size = 1;
3712     player->dynabombs_left = 0;
3713     player->dynabomb_xl = FALSE;
3714
3715     player->MovDir = initial_move_dir;
3716     player->MovPos = 0;
3717     player->GfxPos = 0;
3718     player->GfxDir = initial_move_dir;
3719     player->GfxAction = ACTION_DEFAULT;
3720     player->Frame = 0;
3721     player->StepFrame = 0;
3722
3723     player->initial_element = player->element_nr;
3724     player->artwork_element =
3725       (level.use_artwork_element[i] ? level.artwork_element[i] :
3726        player->element_nr);
3727     player->use_murphy = FALSE;
3728
3729     player->block_last_field = FALSE;   // initialized in InitPlayerField()
3730     player->block_delay_adjustment = 0; // initialized in InitPlayerField()
3731
3732     player->gravity = level.initial_player_gravity[i];
3733
3734     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3735
3736     player->actual_frame_counter.count = 0;
3737     player->actual_frame_counter.value = 1;
3738
3739     player->step_counter = 0;
3740
3741     player->last_move_dir = initial_move_dir;
3742
3743     player->is_active = FALSE;
3744
3745     player->is_waiting = FALSE;
3746     player->is_moving = FALSE;
3747     player->is_auto_moving = FALSE;
3748     player->is_digging = FALSE;
3749     player->is_snapping = FALSE;
3750     player->is_collecting = FALSE;
3751     player->is_pushing = FALSE;
3752     player->is_switching = FALSE;
3753     player->is_dropping = FALSE;
3754     player->is_dropping_pressed = FALSE;
3755
3756     player->is_bored = FALSE;
3757     player->is_sleeping = FALSE;
3758
3759     player->was_waiting = TRUE;
3760     player->was_moving = FALSE;
3761     player->was_snapping = FALSE;
3762     player->was_dropping = FALSE;
3763
3764     player->force_dropping = FALSE;
3765
3766     player->frame_counter_bored = -1;
3767     player->frame_counter_sleeping = -1;
3768
3769     player->anim_delay_counter = 0;
3770     player->post_delay_counter = 0;
3771
3772     player->dir_waiting = initial_move_dir;
3773     player->action_waiting = ACTION_DEFAULT;
3774     player->last_action_waiting = ACTION_DEFAULT;
3775     player->special_action_bored = ACTION_DEFAULT;
3776     player->special_action_sleeping = ACTION_DEFAULT;
3777
3778     player->switch_x = -1;
3779     player->switch_y = -1;
3780
3781     player->drop_x = -1;
3782     player->drop_y = -1;
3783
3784     player->show_envelope = 0;
3785
3786     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3787
3788     player->push_delay       = -1;      // initialized when pushing starts
3789     player->push_delay_value = game.initial_push_delay_value;
3790
3791     player->drop_delay = 0;
3792     player->drop_pressed_delay = 0;
3793
3794     player->last_jx = -1;
3795     player->last_jy = -1;
3796     player->jx = -1;
3797     player->jy = -1;
3798
3799     player->shield_normal_time_left = 0;
3800     player->shield_deadly_time_left = 0;
3801
3802     player->last_removed_element = EL_UNDEFINED;
3803
3804     player->inventory_infinite_element = EL_UNDEFINED;
3805     player->inventory_size = 0;
3806
3807     if (level.use_initial_inventory[i])
3808     {
3809       for (j = 0; j < level.initial_inventory_size[i]; j++)
3810       {
3811         int element = level.initial_inventory_content[i][j];
3812         int collect_count = element_info[element].collect_count_initial;
3813         int k;
3814
3815         if (!IS_CUSTOM_ELEMENT(element))
3816           collect_count = 1;
3817
3818         if (collect_count == 0)
3819           player->inventory_infinite_element = element;
3820         else
3821           for (k = 0; k < collect_count; k++)
3822             if (player->inventory_size < MAX_INVENTORY_SIZE)
3823               player->inventory_element[player->inventory_size++] = element;
3824       }
3825     }
3826
3827     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3828     SnapField(player, 0, 0);
3829
3830     map_player_action[i] = i;
3831   }
3832
3833   network_player_action_received = FALSE;
3834
3835   // initial null action
3836   if (network_playing)
3837     SendToServer_MovePlayer(MV_NONE);
3838
3839   FrameCounter = 0;
3840   TimeFrames = 0;
3841   TimePlayed = 0;
3842   TimeLeft = level.time;
3843   TapeTime = 0;
3844
3845   ScreenMovDir = MV_NONE;
3846   ScreenMovPos = 0;
3847   ScreenGfxPos = 0;
3848
3849   ScrollStepSize = 0;   // will be correctly initialized by ScrollScreen()
3850
3851   game.robot_wheel_x = -1;
3852   game.robot_wheel_y = -1;
3853
3854   game.exit_x = -1;
3855   game.exit_y = -1;
3856
3857   game.all_players_gone = FALSE;
3858
3859   game.LevelSolved = FALSE;
3860   game.GameOver = FALSE;
3861
3862   game.GamePlayed = !tape.playing;
3863
3864   game.LevelSolved_GameWon = FALSE;
3865   game.LevelSolved_GameEnd = FALSE;
3866   game.LevelSolved_SaveTape = FALSE;
3867   game.LevelSolved_SaveScore = FALSE;
3868
3869   game.LevelSolved_CountingTime = 0;
3870   game.LevelSolved_CountingScore = 0;
3871   game.LevelSolved_CountingHealth = 0;
3872
3873   game.panel.active = TRUE;
3874
3875   game.no_level_time_limit = (level.time == 0);
3876   game.time_limit = (leveldir_current->time_limit && setup.time_limit);
3877
3878   game.yamyam_content_nr = 0;
3879   game.robot_wheel_active = FALSE;
3880   game.magic_wall_active = FALSE;
3881   game.magic_wall_time_left = 0;
3882   game.light_time_left = 0;
3883   game.timegate_time_left = 0;
3884   game.switchgate_pos = 0;
3885   game.wind_direction = level.wind_direction_initial;
3886
3887   game.time_final = 0;
3888   game.score_time_final = 0;
3889
3890   game.score = 0;
3891   game.score_final = 0;
3892
3893   game.health = MAX_HEALTH;
3894   game.health_final = MAX_HEALTH;
3895
3896   game.gems_still_needed = level.gems_needed;
3897   game.sokoban_fields_still_needed = 0;
3898   game.sokoban_objects_still_needed = 0;
3899   game.lights_still_needed = 0;
3900   game.players_still_needed = 0;
3901   game.friends_still_needed = 0;
3902
3903   game.lenses_time_left = 0;
3904   game.magnify_time_left = 0;
3905
3906   game.ball_active = level.ball_active_initial;
3907   game.ball_content_nr = 0;
3908
3909   game.explosions_delayed = TRUE;
3910
3911   game.envelope_active = FALSE;
3912
3913   // special case: set custom artwork setting to initial value
3914   game.use_masked_elements = game.use_masked_elements_initial;
3915
3916   for (i = 0; i < NUM_BELTS; i++)
3917   {
3918     game.belt_dir[i] = MV_NONE;
3919     game.belt_dir_nr[i] = 3;            // not moving, next moving left
3920   }
3921
3922   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3923     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3924
3925 #if DEBUG_INIT_PLAYER
3926   DebugPrintPlayerStatus("Player status at level initialization");
3927 #endif
3928
3929   SCAN_PLAYFIELD(x, y)
3930   {
3931     Tile[x][y] = Last[x][y] = level.field[x][y];
3932     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3933     ChangeDelay[x][y] = 0;
3934     ChangePage[x][y] = -1;
3935     CustomValue[x][y] = 0;              // initialized in InitField()
3936     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3937     AmoebaNr[x][y] = 0;
3938     WasJustMoving[x][y] = 0;
3939     WasJustFalling[x][y] = 0;
3940     CheckCollision[x][y] = 0;
3941     CheckImpact[x][y] = 0;
3942     Stop[x][y] = FALSE;
3943     Pushed[x][y] = FALSE;
3944
3945     ChangeCount[x][y] = 0;
3946     ChangeEvent[x][y] = -1;
3947
3948     ExplodePhase[x][y] = 0;
3949     ExplodeDelay[x][y] = 0;
3950     ExplodeField[x][y] = EX_TYPE_NONE;
3951
3952     RunnerVisit[x][y] = 0;
3953     PlayerVisit[x][y] = 0;
3954
3955     GfxFrame[x][y] = 0;
3956     GfxRandom[x][y] = INIT_GFX_RANDOM();
3957     GfxRandomStatic[x][y] = INIT_GFX_RANDOM();
3958     GfxElement[x][y] = EL_UNDEFINED;
3959     GfxElementEmpty[x][y] = EL_EMPTY;
3960     GfxAction[x][y] = ACTION_DEFAULT;
3961     GfxDir[x][y] = MV_NONE;
3962     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3963   }
3964
3965   SCAN_PLAYFIELD(x, y)
3966   {
3967     if (emulate_bd && !IS_BD_ELEMENT(Tile[x][y]))
3968       emulate_bd = FALSE;
3969     if (emulate_sp && !IS_SP_ELEMENT(Tile[x][y]))
3970       emulate_sp = FALSE;
3971
3972     InitField(x, y, TRUE);
3973
3974     ResetGfxAnimation(x, y);
3975   }
3976
3977   InitBeltMovement();
3978
3979   // required if level does not contain any "empty space" element
3980   if (element_info[EL_EMPTY].use_gfx_element)
3981     game.use_masked_elements = TRUE;
3982
3983   for (i = 0; i < MAX_PLAYERS; i++)
3984   {
3985     struct PlayerInfo *player = &stored_player[i];
3986
3987     // set number of special actions for bored and sleeping animation
3988     player->num_special_action_bored =
3989       get_num_special_action(player->artwork_element,
3990                              ACTION_BORING_1, ACTION_BORING_LAST);
3991     player->num_special_action_sleeping =
3992       get_num_special_action(player->artwork_element,
3993                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3994   }
3995
3996   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3997                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3998
3999   // initialize type of slippery elements
4000   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4001   {
4002     if (!IS_CUSTOM_ELEMENT(i))
4003     {
4004       // default: elements slip down either to the left or right randomly
4005       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
4006
4007       // SP style elements prefer to slip down on the left side
4008       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
4009         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
4010
4011       // BD style elements prefer to slip down on the left side
4012       if (game.emulation == EMU_BOULDERDASH)
4013         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
4014     }
4015   }
4016
4017   // initialize explosion and ignition delay
4018   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4019   {
4020     if (!IS_CUSTOM_ELEMENT(i))
4021     {
4022       int num_phase = 8;
4023       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
4024                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
4025                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
4026       int last_phase = (num_phase + 1) * delay;
4027       int half_phase = (num_phase / 2) * delay;
4028
4029       element_info[i].explosion_delay = last_phase - 1;
4030       element_info[i].ignition_delay = half_phase;
4031
4032       if (i == EL_BLACK_ORB)
4033         element_info[i].ignition_delay = 1;
4034     }
4035   }
4036
4037   // correct non-moving belts to start moving left
4038   for (i = 0; i < NUM_BELTS; i++)
4039     if (game.belt_dir[i] == MV_NONE)
4040       game.belt_dir_nr[i] = 3;          // not moving, next moving left
4041
4042 #if USE_NEW_PLAYER_ASSIGNMENTS
4043   // use preferred player also in local single-player mode
4044   if (!network.enabled && !game.team_mode)
4045   {
4046     int new_index_nr = setup.network_player_nr;
4047
4048     if (new_index_nr >= 0 && new_index_nr < MAX_PLAYERS)
4049     {
4050       for (i = 0; i < MAX_PLAYERS; i++)
4051         stored_player[i].connected_locally = FALSE;
4052
4053       stored_player[new_index_nr].connected_locally = TRUE;
4054     }
4055   }
4056
4057   for (i = 0; i < MAX_PLAYERS; i++)
4058   {
4059     stored_player[i].connected = FALSE;
4060
4061     // in network game mode, the local player might not be the first player
4062     if (stored_player[i].connected_locally)
4063       local_player = &stored_player[i];
4064   }
4065
4066   if (!network.enabled)
4067     local_player->connected = TRUE;
4068
4069   if (tape.playing)
4070   {
4071     for (i = 0; i < MAX_PLAYERS; i++)
4072       stored_player[i].connected = tape.player_participates[i];
4073   }
4074   else if (network.enabled)
4075   {
4076     // add team mode players connected over the network (needed for correct
4077     // assignment of player figures from level to locally playing players)
4078
4079     for (i = 0; i < MAX_PLAYERS; i++)
4080       if (stored_player[i].connected_network)
4081         stored_player[i].connected = TRUE;
4082   }
4083   else if (game.team_mode)
4084   {
4085     // try to guess locally connected team mode players (needed for correct
4086     // assignment of player figures from level to locally playing players)
4087
4088     for (i = 0; i < MAX_PLAYERS; i++)
4089       if (setup.input[i].use_joystick ||
4090           setup.input[i].key.left != KSYM_UNDEFINED)
4091         stored_player[i].connected = TRUE;
4092   }
4093
4094 #if DEBUG_INIT_PLAYER
4095   DebugPrintPlayerStatus("Player status after level initialization");
4096 #endif
4097
4098 #if DEBUG_INIT_PLAYER
4099   Debug("game:init:player", "Reassigning players ...");
4100 #endif
4101
4102   // check if any connected player was not found in playfield
4103   for (i = 0; i < MAX_PLAYERS; i++)
4104   {
4105     struct PlayerInfo *player = &stored_player[i];
4106
4107     if (player->connected && !player->present)
4108     {
4109       struct PlayerInfo *field_player = NULL;
4110
4111 #if DEBUG_INIT_PLAYER
4112       Debug("game:init:player",
4113             "- looking for field player for player %d ...", i + 1);
4114 #endif
4115
4116       // assign first free player found that is present in the playfield
4117
4118       // first try: look for unmapped playfield player that is not connected
4119       for (j = 0; j < MAX_PLAYERS; j++)
4120         if (field_player == NULL &&
4121             stored_player[j].present &&
4122             !stored_player[j].mapped &&
4123             !stored_player[j].connected)
4124           field_player = &stored_player[j];
4125
4126       // second try: look for *any* unmapped playfield player
4127       for (j = 0; j < MAX_PLAYERS; j++)
4128         if (field_player == NULL &&
4129             stored_player[j].present &&
4130             !stored_player[j].mapped)
4131           field_player = &stored_player[j];
4132
4133       if (field_player != NULL)
4134       {
4135         int jx = field_player->jx, jy = field_player->jy;
4136
4137 #if DEBUG_INIT_PLAYER
4138         Debug("game:init:player", "- found player %d",
4139               field_player->index_nr + 1);
4140 #endif
4141
4142         player->present = FALSE;
4143         player->active = FALSE;
4144
4145         field_player->present = TRUE;
4146         field_player->active = TRUE;
4147
4148         /*
4149         player->initial_element = field_player->initial_element;
4150         player->artwork_element = field_player->artwork_element;
4151
4152         player->block_last_field       = field_player->block_last_field;
4153         player->block_delay_adjustment = field_player->block_delay_adjustment;
4154         */
4155
4156         StorePlayer[jx][jy] = field_player->element_nr;
4157
4158         field_player->jx = field_player->last_jx = jx;
4159         field_player->jy = field_player->last_jy = jy;
4160
4161         if (local_player == player)
4162           local_player = field_player;
4163
4164         map_player_action[field_player->index_nr] = i;
4165
4166         field_player->mapped = TRUE;
4167
4168 #if DEBUG_INIT_PLAYER
4169         Debug("game:init:player", "- map_player_action[%d] == %d",
4170               field_player->index_nr + 1, i + 1);
4171 #endif
4172       }
4173     }
4174
4175     if (player->connected && player->present)
4176       player->mapped = TRUE;
4177   }
4178
4179 #if DEBUG_INIT_PLAYER
4180   DebugPrintPlayerStatus("Player status after player assignment (first stage)");
4181 #endif
4182
4183 #else
4184
4185   // check if any connected player was not found in playfield
4186   for (i = 0; i < MAX_PLAYERS; i++)
4187   {
4188     struct PlayerInfo *player = &stored_player[i];
4189
4190     if (player->connected && !player->present)
4191     {
4192       for (j = 0; j < MAX_PLAYERS; j++)
4193       {
4194         struct PlayerInfo *field_player = &stored_player[j];
4195         int jx = field_player->jx, jy = field_player->jy;
4196
4197         // assign first free player found that is present in the playfield
4198         if (field_player->present && !field_player->connected)
4199         {
4200           player->present = TRUE;
4201           player->active = TRUE;
4202
4203           field_player->present = FALSE;
4204           field_player->active = FALSE;
4205
4206           player->initial_element = field_player->initial_element;
4207           player->artwork_element = field_player->artwork_element;
4208
4209           player->block_last_field       = field_player->block_last_field;
4210           player->block_delay_adjustment = field_player->block_delay_adjustment;
4211
4212           StorePlayer[jx][jy] = player->element_nr;
4213
4214           player->jx = player->last_jx = jx;
4215           player->jy = player->last_jy = jy;
4216
4217           break;
4218         }
4219       }
4220     }
4221   }
4222 #endif
4223
4224 #if 0
4225   Debug("game:init:player", "local_player->present == %d",
4226         local_player->present);
4227 #endif
4228
4229   // set focus to local player for network games, else to all players
4230   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
4231   game.centered_player_nr_next = game.centered_player_nr;
4232   game.set_centered_player = FALSE;
4233   game.set_centered_player_wrap = FALSE;
4234
4235   if (network_playing && tape.recording)
4236   {
4237     // store client dependent player focus when recording network games
4238     tape.centered_player_nr_next = game.centered_player_nr_next;
4239     tape.set_centered_player = TRUE;
4240   }
4241
4242   if (tape.playing)
4243   {
4244     // when playing a tape, eliminate all players who do not participate
4245
4246 #if USE_NEW_PLAYER_ASSIGNMENTS
4247
4248     if (!game.team_mode)
4249     {
4250       for (i = 0; i < MAX_PLAYERS; i++)
4251       {
4252         if (stored_player[i].active &&
4253             !tape.player_participates[map_player_action[i]])
4254         {
4255           struct PlayerInfo *player = &stored_player[i];
4256           int jx = player->jx, jy = player->jy;
4257
4258 #if DEBUG_INIT_PLAYER
4259           Debug("game:init:player", "Removing player %d at (%d, %d)",
4260                 i + 1, jx, jy);
4261 #endif
4262
4263           player->active = FALSE;
4264           StorePlayer[jx][jy] = 0;
4265           Tile[jx][jy] = EL_EMPTY;
4266         }
4267       }
4268     }
4269
4270 #else
4271
4272     for (i = 0; i < MAX_PLAYERS; i++)
4273     {
4274       if (stored_player[i].active &&
4275           !tape.player_participates[i])
4276       {
4277         struct PlayerInfo *player = &stored_player[i];
4278         int jx = player->jx, jy = player->jy;
4279
4280         player->active = FALSE;
4281         StorePlayer[jx][jy] = 0;
4282         Tile[jx][jy] = EL_EMPTY;
4283       }
4284     }
4285 #endif
4286   }
4287   else if (!network.enabled && !game.team_mode)         // && !tape.playing
4288   {
4289     // when in single player mode, eliminate all but the local player
4290
4291     for (i = 0; i < MAX_PLAYERS; i++)
4292     {
4293       struct PlayerInfo *player = &stored_player[i];
4294
4295       if (player->active && player != local_player)
4296       {
4297         int jx = player->jx, jy = player->jy;
4298
4299         player->active = FALSE;
4300         player->present = FALSE;
4301
4302         StorePlayer[jx][jy] = 0;
4303         Tile[jx][jy] = EL_EMPTY;
4304       }
4305     }
4306   }
4307
4308   for (i = 0; i < MAX_PLAYERS; i++)
4309     if (stored_player[i].active)
4310       game.players_still_needed++;
4311
4312   if (level.solved_by_one_player)
4313     game.players_still_needed = 1;
4314
4315   // when recording the game, store which players take part in the game
4316   if (tape.recording)
4317   {
4318 #if USE_NEW_PLAYER_ASSIGNMENTS
4319     for (i = 0; i < MAX_PLAYERS; i++)
4320       if (stored_player[i].connected)
4321         tape.player_participates[i] = TRUE;
4322 #else
4323     for (i = 0; i < MAX_PLAYERS; i++)
4324       if (stored_player[i].active)
4325         tape.player_participates[i] = TRUE;
4326 #endif
4327   }
4328
4329 #if DEBUG_INIT_PLAYER
4330   DebugPrintPlayerStatus("Player status after player assignment (final stage)");
4331 #endif
4332
4333   if (BorderElement == EL_EMPTY)
4334   {
4335     SBX_Left = 0;
4336     SBX_Right = lev_fieldx - SCR_FIELDX;
4337     SBY_Upper = 0;
4338     SBY_Lower = lev_fieldy - SCR_FIELDY;
4339   }
4340   else
4341   {
4342     SBX_Left = -1;
4343     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4344     SBY_Upper = -1;
4345     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4346   }
4347
4348   if (full_lev_fieldx <= SCR_FIELDX)
4349     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4350   if (full_lev_fieldy <= SCR_FIELDY)
4351     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4352
4353   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4354     SBX_Left--;
4355   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4356     SBY_Upper--;
4357
4358   // if local player not found, look for custom element that might create
4359   // the player (make some assumptions about the right custom element)
4360   if (!local_player->present)
4361   {
4362     int start_x = 0, start_y = 0;
4363     int found_rating = 0;
4364     int found_element = EL_UNDEFINED;
4365     int player_nr = local_player->index_nr;
4366
4367     SCAN_PLAYFIELD(x, y)
4368     {
4369       int element = Tile[x][y];
4370       int content;
4371       int xx, yy;
4372       boolean is_player;
4373
4374       if (level.use_start_element[player_nr] &&
4375           level.start_element[player_nr] == element &&
4376           found_rating < 4)
4377       {
4378         start_x = x;
4379         start_y = y;
4380
4381         found_rating = 4;
4382         found_element = element;
4383       }
4384
4385       if (!IS_CUSTOM_ELEMENT(element))
4386         continue;
4387
4388       if (CAN_CHANGE(element))
4389       {
4390         for (i = 0; i < element_info[element].num_change_pages; i++)
4391         {
4392           // check for player created from custom element as single target
4393           content = element_info[element].change_page[i].target_element;
4394           is_player = IS_PLAYER_ELEMENT(content);
4395
4396           if (is_player && (found_rating < 3 ||
4397                             (found_rating == 3 && element < found_element)))
4398           {
4399             start_x = x;
4400             start_y = y;
4401
4402             found_rating = 3;
4403             found_element = element;
4404           }
4405         }
4406       }
4407
4408       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4409       {
4410         // check for player created from custom element as explosion content
4411         content = element_info[element].content.e[xx][yy];
4412         is_player = IS_PLAYER_ELEMENT(content);
4413
4414         if (is_player && (found_rating < 2 ||
4415                           (found_rating == 2 && element < found_element)))
4416         {
4417           start_x = x + xx - 1;
4418           start_y = y + yy - 1;
4419
4420           found_rating = 2;
4421           found_element = element;
4422         }
4423
4424         if (!CAN_CHANGE(element))
4425           continue;
4426
4427         for (i = 0; i < element_info[element].num_change_pages; i++)
4428         {
4429           // check for player created from custom element as extended target
4430           content =
4431             element_info[element].change_page[i].target_content.e[xx][yy];
4432
4433           is_player = IS_PLAYER_ELEMENT(content);
4434
4435           if (is_player && (found_rating < 1 ||
4436                             (found_rating == 1 && element < found_element)))
4437           {
4438             start_x = x + xx - 1;
4439             start_y = y + yy - 1;
4440
4441             found_rating = 1;
4442             found_element = element;
4443           }
4444         }
4445       }
4446     }
4447
4448     scroll_x = SCROLL_POSITION_X(start_x);
4449     scroll_y = SCROLL_POSITION_Y(start_y);
4450   }
4451   else
4452   {
4453     scroll_x = SCROLL_POSITION_X(local_player->jx);
4454     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4455   }
4456
4457   if (game.forced_scroll_x != ARG_UNDEFINED_VALUE)
4458     scroll_x = game.forced_scroll_x;
4459   if (game.forced_scroll_y != ARG_UNDEFINED_VALUE)
4460     scroll_y = game.forced_scroll_y;
4461
4462   // !!! FIX THIS (START) !!!
4463   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4464   {
4465     InitGameEngine_EM();
4466   }
4467   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4468   {
4469     InitGameEngine_SP();
4470   }
4471   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4472   {
4473     InitGameEngine_MM();
4474   }
4475   else
4476   {
4477     DrawLevel(REDRAW_FIELD);
4478     DrawAllPlayers();
4479
4480     // after drawing the level, correct some elements
4481     if (game.timegate_time_left == 0)
4482       CloseAllOpenTimegates();
4483   }
4484
4485   // blit playfield from scroll buffer to normal back buffer for fading in
4486   BlitScreenToBitmap(backbuffer);
4487   // !!! FIX THIS (END) !!!
4488
4489   DrawMaskedBorder(fade_mask);
4490
4491   FadeIn(fade_mask);
4492
4493 #if 1
4494   // full screen redraw is required at this point in the following cases:
4495   // - special editor door undrawn when game was started from level editor
4496   // - drawing area (playfield) was changed and has to be removed completely
4497   redraw_mask = REDRAW_ALL;
4498   BackToFront();
4499 #endif
4500
4501   if (!game.restart_level)
4502   {
4503     // copy default game door content to main double buffer
4504
4505     // !!! CHECK AGAIN !!!
4506     SetPanelBackground();
4507     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4508     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4509   }
4510
4511   SetPanelBackground();
4512   SetDrawBackgroundMask(REDRAW_DOOR_1);
4513
4514   UpdateAndDisplayGameControlValues();
4515
4516   if (!game.restart_level)
4517   {
4518     UnmapGameButtons();
4519     UnmapTapeButtons();
4520
4521     FreeGameButtons();
4522     CreateGameButtons();
4523
4524     MapGameButtons();
4525     MapTapeButtons();
4526
4527     // copy actual game door content to door double buffer for OpenDoor()
4528     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4529
4530     OpenDoor(DOOR_OPEN_ALL);
4531
4532     KeyboardAutoRepeatOffUnlessAutoplay();
4533
4534 #if DEBUG_INIT_PLAYER
4535     DebugPrintPlayerStatus("Player status (final)");
4536 #endif
4537   }
4538
4539   UnmapAllGadgets();
4540
4541   MapGameButtons();
4542   MapTapeButtons();
4543
4544   if (!game.restart_level && !tape.playing)
4545   {
4546     LevelStats_incPlayed(level_nr);
4547
4548     SaveLevelSetup_SeriesInfo();
4549   }
4550
4551   game.restart_level = FALSE;
4552   game.request_active = FALSE;
4553
4554   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4555     InitGameActions_MM();
4556
4557   SaveEngineSnapshotToListInitial();
4558
4559   if (!game.restart_level)
4560   {
4561     PlaySound(SND_GAME_STARTING);
4562
4563     if (setup.sound_music)
4564       PlayLevelMusic();
4565   }
4566
4567   SetPlayfieldMouseCursorEnabled(!game.use_mouse_actions);
4568 }
4569
4570 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4571                         int actual_player_x, int actual_player_y)
4572 {
4573   // this is used for non-R'n'D game engines to update certain engine values
4574
4575   // needed to determine if sounds are played within the visible screen area
4576   scroll_x = actual_scroll_x;
4577   scroll_y = actual_scroll_y;
4578
4579   // needed to get player position for "follow finger" playing input method
4580   local_player->jx = actual_player_x;
4581   local_player->jy = actual_player_y;
4582 }
4583
4584 void InitMovDir(int x, int y)
4585 {
4586   int i, element = Tile[x][y];
4587   static int xy[4][2] =
4588   {
4589     {  0, +1 },
4590     { +1,  0 },
4591     {  0, -1 },
4592     { -1,  0 }
4593   };
4594   static int direction[3][4] =
4595   {
4596     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4597     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4598     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4599   };
4600
4601   switch (element)
4602   {
4603     case EL_BUG_RIGHT:
4604     case EL_BUG_UP:
4605     case EL_BUG_LEFT:
4606     case EL_BUG_DOWN:
4607       Tile[x][y] = EL_BUG;
4608       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4609       break;
4610
4611     case EL_SPACESHIP_RIGHT:
4612     case EL_SPACESHIP_UP:
4613     case EL_SPACESHIP_LEFT:
4614     case EL_SPACESHIP_DOWN:
4615       Tile[x][y] = EL_SPACESHIP;
4616       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4617       break;
4618
4619     case EL_BD_BUTTERFLY_RIGHT:
4620     case EL_BD_BUTTERFLY_UP:
4621     case EL_BD_BUTTERFLY_LEFT:
4622     case EL_BD_BUTTERFLY_DOWN:
4623       Tile[x][y] = EL_BD_BUTTERFLY;
4624       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4625       break;
4626
4627     case EL_BD_FIREFLY_RIGHT:
4628     case EL_BD_FIREFLY_UP:
4629     case EL_BD_FIREFLY_LEFT:
4630     case EL_BD_FIREFLY_DOWN:
4631       Tile[x][y] = EL_BD_FIREFLY;
4632       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4633       break;
4634
4635     case EL_PACMAN_RIGHT:
4636     case EL_PACMAN_UP:
4637     case EL_PACMAN_LEFT:
4638     case EL_PACMAN_DOWN:
4639       Tile[x][y] = EL_PACMAN;
4640       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4641       break;
4642
4643     case EL_YAMYAM_LEFT:
4644     case EL_YAMYAM_RIGHT:
4645     case EL_YAMYAM_UP:
4646     case EL_YAMYAM_DOWN:
4647       Tile[x][y] = EL_YAMYAM;
4648       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4649       break;
4650
4651     case EL_SP_SNIKSNAK:
4652       MovDir[x][y] = MV_UP;
4653       break;
4654
4655     case EL_SP_ELECTRON:
4656       MovDir[x][y] = MV_LEFT;
4657       break;
4658
4659     case EL_MOLE_LEFT:
4660     case EL_MOLE_RIGHT:
4661     case EL_MOLE_UP:
4662     case EL_MOLE_DOWN:
4663       Tile[x][y] = EL_MOLE;
4664       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4665       break;
4666
4667     case EL_SPRING_LEFT:
4668     case EL_SPRING_RIGHT:
4669       Tile[x][y] = EL_SPRING;
4670       MovDir[x][y] = direction[2][element - EL_SPRING_LEFT];
4671       break;
4672
4673     default:
4674       if (IS_CUSTOM_ELEMENT(element))
4675       {
4676         struct ElementInfo *ei = &element_info[element];
4677         int move_direction_initial = ei->move_direction_initial;
4678         int move_pattern = ei->move_pattern;
4679
4680         if (move_direction_initial == MV_START_PREVIOUS)
4681         {
4682           if (MovDir[x][y] != MV_NONE)
4683             return;
4684
4685           move_direction_initial = MV_START_AUTOMATIC;
4686         }
4687
4688         if (move_direction_initial == MV_START_RANDOM)
4689           MovDir[x][y] = 1 << RND(4);
4690         else if (move_direction_initial & MV_ANY_DIRECTION)
4691           MovDir[x][y] = move_direction_initial;
4692         else if (move_pattern == MV_ALL_DIRECTIONS ||
4693                  move_pattern == MV_TURNING_LEFT ||
4694                  move_pattern == MV_TURNING_RIGHT ||
4695                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4696                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4697                  move_pattern == MV_TURNING_RANDOM)
4698           MovDir[x][y] = 1 << RND(4);
4699         else if (move_pattern == MV_HORIZONTAL)
4700           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4701         else if (move_pattern == MV_VERTICAL)
4702           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4703         else if (move_pattern & MV_ANY_DIRECTION)
4704           MovDir[x][y] = element_info[element].move_pattern;
4705         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4706                  move_pattern == MV_ALONG_RIGHT_SIDE)
4707         {
4708           // use random direction as default start direction
4709           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4710             MovDir[x][y] = 1 << RND(4);
4711
4712           for (i = 0; i < NUM_DIRECTIONS; i++)
4713           {
4714             int x1 = x + xy[i][0];
4715             int y1 = y + xy[i][1];
4716
4717             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4718             {
4719               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4720                 MovDir[x][y] = direction[0][i];
4721               else
4722                 MovDir[x][y] = direction[1][i];
4723
4724               break;
4725             }
4726           }
4727         }                
4728       }
4729       else
4730       {
4731         MovDir[x][y] = 1 << RND(4);
4732
4733         if (element != EL_BUG &&
4734             element != EL_SPACESHIP &&
4735             element != EL_BD_BUTTERFLY &&
4736             element != EL_BD_FIREFLY)
4737           break;
4738
4739         for (i = 0; i < NUM_DIRECTIONS; i++)
4740         {
4741           int x1 = x + xy[i][0];
4742           int y1 = y + xy[i][1];
4743
4744           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4745           {
4746             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4747             {
4748               MovDir[x][y] = direction[0][i];
4749               break;
4750             }
4751             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4752                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4753             {
4754               MovDir[x][y] = direction[1][i];
4755               break;
4756             }
4757           }
4758         }
4759       }
4760       break;
4761   }
4762
4763   GfxDir[x][y] = MovDir[x][y];
4764 }
4765
4766 void InitAmoebaNr(int x, int y)
4767 {
4768   int i;
4769   int group_nr = AmoebaNeighbourNr(x, y);
4770
4771   if (group_nr == 0)
4772   {
4773     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4774     {
4775       if (AmoebaCnt[i] == 0)
4776       {
4777         group_nr = i;
4778         break;
4779       }
4780     }
4781   }
4782
4783   AmoebaNr[x][y] = group_nr;
4784   AmoebaCnt[group_nr]++;
4785   AmoebaCnt2[group_nr]++;
4786 }
4787
4788 static void LevelSolved_SetFinalGameValues(void)
4789 {
4790   game.time_final = (game.no_level_time_limit ? TimePlayed : TimeLeft);
4791   game.score_time_final = (level.use_step_counter ? TimePlayed :
4792                            TimePlayed * FRAMES_PER_SECOND + TimeFrames);
4793
4794   game.score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4795                       game_em.lev->score :
4796                       level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4797                       game_mm.score :
4798                       game.score);
4799
4800   game.health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4801                        MM_HEALTH(game_mm.laser_overload_value) :
4802                        game.health);
4803
4804   game.LevelSolved_CountingTime = game.time_final;
4805   game.LevelSolved_CountingScore = game.score_final;
4806   game.LevelSolved_CountingHealth = game.health_final;
4807 }
4808
4809 static void LevelSolved_DisplayFinalGameValues(int time, int score, int health)
4810 {
4811   game.LevelSolved_CountingTime = time;
4812   game.LevelSolved_CountingScore = score;
4813   game.LevelSolved_CountingHealth = health;
4814
4815   game_panel_controls[GAME_PANEL_TIME].value = time;
4816   game_panel_controls[GAME_PANEL_SCORE].value = score;
4817   game_panel_controls[GAME_PANEL_HEALTH].value = health;
4818
4819   DisplayGameControlValues();
4820 }
4821
4822 static void LevelSolved(void)
4823 {
4824   if (level.game_engine_type == GAME_ENGINE_TYPE_RND &&
4825       game.players_still_needed > 0)
4826     return;
4827
4828   game.LevelSolved = TRUE;
4829   game.GameOver = TRUE;
4830
4831   tape.solved = TRUE;
4832
4833   // needed here to display correct panel values while player walks into exit
4834   LevelSolved_SetFinalGameValues();
4835 }
4836
4837 void GameWon(void)
4838 {
4839   static int time_count_steps;
4840   static int time, time_final;
4841   static float score, score_final; // needed for time score < 10 for 10 seconds
4842   static int health, health_final;
4843   static int game_over_delay_1 = 0;
4844   static int game_over_delay_2 = 0;
4845   static int game_over_delay_3 = 0;
4846   int time_score_base = MIN(MAX(1, level.time_score_base), 10);
4847   float time_score = (float)level.score[SC_TIME_BONUS] / time_score_base;
4848
4849   if (!game.LevelSolved_GameWon)
4850   {
4851     int i;
4852
4853     // do not start end game actions before the player stops moving (to exit)
4854     if (local_player->active && local_player->MovPos)
4855       return;
4856
4857     // calculate final game values after player finished walking into exit
4858     LevelSolved_SetFinalGameValues();
4859
4860     game.LevelSolved_GameWon = TRUE;
4861     game.LevelSolved_SaveTape = tape.recording;
4862     game.LevelSolved_SaveScore = !tape.playing;
4863
4864     if (!tape.playing)
4865     {
4866       LevelStats_incSolved(level_nr);
4867
4868       SaveLevelSetup_SeriesInfo();
4869     }
4870
4871     if (tape.auto_play)         // tape might already be stopped here
4872       tape.auto_play_level_solved = TRUE;
4873
4874     TapeStop();
4875
4876     game_over_delay_1 = FRAMES_PER_SECOND;      // delay before counting time
4877     game_over_delay_2 = FRAMES_PER_SECOND / 2;  // delay before counting health
4878     game_over_delay_3 = FRAMES_PER_SECOND;      // delay before ending the game
4879
4880     time = time_final = game.time_final;
4881     score = score_final = game.score_final;
4882     health = health_final = game.health_final;
4883
4884     // update game panel values before (delayed) counting of score (if any)
4885     LevelSolved_DisplayFinalGameValues(time, score, health);
4886
4887     // if level has time score defined, calculate new final game values
4888     if (time_score > 0)
4889     {
4890       int time_final_max = 999;
4891       int time_frames_final_max = time_final_max * FRAMES_PER_SECOND;
4892       int time_frames = 0;
4893       int time_frames_left = TimeLeft * FRAMES_PER_SECOND - TimeFrames;
4894       int time_frames_played = TimePlayed * FRAMES_PER_SECOND + TimeFrames;
4895
4896       if (TimeLeft > 0)
4897       {
4898         time_final = 0;
4899         time_frames = time_frames_left;
4900       }
4901       else if (game.no_level_time_limit && TimePlayed < time_final_max)
4902       {
4903         time_final = time_final_max;
4904         time_frames = time_frames_final_max - time_frames_played;
4905       }
4906
4907       score_final += time_score * time_frames / FRAMES_PER_SECOND + 0.5;
4908
4909       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4910
4911       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4912       {
4913         health_final = 0;
4914         score_final += health * time_score;
4915       }
4916
4917       game.score_final = score_final;
4918       game.health_final = health_final;
4919     }
4920
4921     // if not counting score after game, immediately update game panel values
4922     if (level_editor_test_game || !setup.count_score_after_game)
4923     {
4924       time = time_final;
4925       score = score_final;
4926
4927       LevelSolved_DisplayFinalGameValues(time, score, health);
4928     }
4929
4930     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4931     {
4932       // check if last player has left the level
4933       if (game.exit_x >= 0 &&
4934           game.exit_y >= 0)
4935       {
4936         int x = game.exit_x;
4937         int y = game.exit_y;
4938         int element = Tile[x][y];
4939
4940         // close exit door after last player
4941         if ((game.all_players_gone &&
4942              (element == EL_EXIT_OPEN ||
4943               element == EL_SP_EXIT_OPEN ||
4944               element == EL_STEEL_EXIT_OPEN)) ||
4945             element == EL_EM_EXIT_OPEN ||
4946             element == EL_EM_STEEL_EXIT_OPEN)
4947         {
4948
4949           Tile[x][y] =
4950             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4951              element == EL_EM_EXIT_OPEN         ? EL_EM_EXIT_CLOSING :
4952              element == EL_SP_EXIT_OPEN         ? EL_SP_EXIT_CLOSING:
4953              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4954              EL_EM_STEEL_EXIT_CLOSING);
4955
4956           PlayLevelSoundElementAction(x, y, element, ACTION_CLOSING);
4957         }
4958
4959         // player disappears
4960         DrawLevelField(x, y);
4961       }
4962
4963       for (i = 0; i < MAX_PLAYERS; i++)
4964       {
4965         struct PlayerInfo *player = &stored_player[i];
4966
4967         if (player->present)
4968         {
4969           RemovePlayer(player);
4970
4971           // player disappears
4972           DrawLevelField(player->jx, player->jy);
4973         }
4974       }
4975     }
4976
4977     PlaySound(SND_GAME_WINNING);
4978   }
4979
4980   if (setup.count_score_after_game)
4981   {
4982     if (time != time_final)
4983     {
4984       if (game_over_delay_1 > 0)
4985       {
4986         game_over_delay_1--;
4987
4988         return;
4989       }
4990
4991       int time_to_go = ABS(time_final - time);
4992       int time_count_dir = (time < time_final ? +1 : -1);
4993
4994       if (time_to_go < time_count_steps)
4995         time_count_steps = 1;
4996
4997       time  += time_count_steps * time_count_dir;
4998       score += time_count_steps * time_score;
4999
5000       // set final score to correct rounding differences after counting score
5001       if (time == time_final)
5002         score = score_final;
5003
5004       LevelSolved_DisplayFinalGameValues(time, score, health);
5005
5006       if (time == time_final)
5007         StopSound(SND_GAME_LEVELTIME_BONUS);
5008       else if (setup.sound_loops)
5009         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
5010       else
5011         PlaySound(SND_GAME_LEVELTIME_BONUS);
5012
5013       return;
5014     }
5015
5016     if (health != health_final)
5017     {
5018       if (game_over_delay_2 > 0)
5019       {
5020         game_over_delay_2--;
5021
5022         return;
5023       }
5024
5025       int health_count_dir = (health < health_final ? +1 : -1);
5026
5027       health += health_count_dir;
5028       score  += time_score;
5029
5030       LevelSolved_DisplayFinalGameValues(time, score, health);
5031
5032       if (health == health_final)
5033         StopSound(SND_GAME_LEVELTIME_BONUS);
5034       else if (setup.sound_loops)
5035         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
5036       else
5037         PlaySound(SND_GAME_LEVELTIME_BONUS);
5038
5039       return;
5040     }
5041   }
5042
5043   game.panel.active = FALSE;
5044
5045   if (game_over_delay_3 > 0)
5046   {
5047     game_over_delay_3--;
5048
5049     return;
5050   }
5051
5052   GameEnd();
5053 }
5054
5055 void GameEnd(void)
5056 {
5057   // used instead of "level_nr" (needed for network games)
5058   int last_level_nr = levelset.level_nr;
5059   boolean tape_saved = FALSE;
5060
5061   game.LevelSolved_GameEnd = TRUE;
5062
5063   if (game.LevelSolved_SaveTape && !score_info_tape_play)
5064   {
5065     // make sure that request dialog to save tape does not open door again
5066     if (!global.use_envelope_request)
5067       CloseDoor(DOOR_CLOSE_1);
5068
5069     // ask to save tape
5070     tape_saved = SaveTapeChecked_LevelSolved(tape.level_nr);
5071
5072     // set unique basename for score tape (also saved in high score table)
5073     strcpy(tape.score_tape_basename, getScoreTapeBasename(setup.player_name));
5074   }
5075
5076   // if no tape is to be saved, close both doors simultaneously
5077   CloseDoor(DOOR_CLOSE_ALL);
5078
5079   if (level_editor_test_game || score_info_tape_play)
5080   {
5081     SetGameStatus(GAME_MODE_MAIN);
5082
5083     DrawMainMenu();
5084
5085     return;
5086   }
5087
5088   if (!game.LevelSolved_SaveScore)
5089   {
5090     SetGameStatus(GAME_MODE_MAIN);
5091
5092     DrawMainMenu();
5093
5094     return;
5095   }
5096
5097   if (level_nr == leveldir_current->handicap_level)
5098   {
5099     leveldir_current->handicap_level++;
5100
5101     SaveLevelSetup_SeriesInfo();
5102   }
5103
5104   // save score and score tape before potentially erasing tape below
5105   NewHighScore(last_level_nr, tape_saved);
5106
5107   if (setup.increment_levels &&
5108       level_nr < leveldir_current->last_level &&
5109       !network_playing)
5110   {
5111     level_nr++;         // advance to next level
5112     TapeErase();        // start with empty tape
5113
5114     if (setup.auto_play_next_level)
5115     {
5116       scores.continue_playing = TRUE;
5117       scores.next_level_nr = level_nr;
5118
5119       LoadLevel(level_nr);
5120
5121       SaveLevelSetup_SeriesInfo();
5122     }
5123   }
5124
5125   if (scores.last_added >= 0 && setup.show_scores_after_game)
5126   {
5127     SetGameStatus(GAME_MODE_SCORES);
5128
5129     DrawHallOfFame(last_level_nr);
5130   }
5131   else if (scores.continue_playing)
5132   {
5133     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
5134   }
5135   else
5136   {
5137     SetGameStatus(GAME_MODE_MAIN);
5138
5139     DrawMainMenu();
5140   }
5141 }
5142
5143 static int addScoreEntry(struct ScoreInfo *list, struct ScoreEntry *new_entry,
5144                          boolean one_score_entry_per_name)
5145 {
5146   int i;
5147
5148   if (strEqual(new_entry->name, EMPTY_PLAYER_NAME))
5149     return -1;
5150
5151   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
5152   {
5153     struct ScoreEntry *entry = &list->entry[i];
5154     boolean score_is_better = (new_entry->score >  entry->score);
5155     boolean score_is_equal  = (new_entry->score == entry->score);
5156     boolean time_is_better  = (new_entry->time  <  entry->time);
5157     boolean time_is_equal   = (new_entry->time  == entry->time);
5158     boolean better_by_score = (score_is_better ||
5159                                (score_is_equal && time_is_better));
5160     boolean better_by_time  = (time_is_better ||
5161                                (time_is_equal && score_is_better));
5162     boolean is_better = (level.rate_time_over_score ? better_by_time :
5163                          better_by_score);
5164     boolean entry_is_empty = (entry->score == 0 &&
5165                               entry->time == 0);
5166
5167     // prevent adding server score entries if also existing in local score file
5168     // (special case: historic score entries have an empty tape basename entry)
5169     if (strEqual(new_entry->tape_basename, entry->tape_basename) &&
5170         !strEqual(new_entry->tape_basename, UNDEFINED_FILENAME))
5171     {
5172       // add fields from server score entry not stored in local score entry
5173       // (currently, this means setting platform, version and country fields;
5174       // in rare cases, this may also correct an invalid score value, as
5175       // historic scores might have been truncated to 16-bit values locally)
5176       *entry = *new_entry;
5177
5178       return -1;
5179     }
5180
5181     if (is_better || entry_is_empty)
5182     {
5183       // player has made it to the hall of fame
5184
5185       if (i < MAX_SCORE_ENTRIES - 1)
5186       {
5187         int m = MAX_SCORE_ENTRIES - 1;
5188         int l;
5189
5190         if (one_score_entry_per_name)
5191         {
5192           for (l = i; l < MAX_SCORE_ENTRIES; l++)
5193             if (strEqual(list->entry[l].name, new_entry->name))
5194               m = l;
5195
5196           if (m == i)   // player's new highscore overwrites his old one
5197             goto put_into_list;
5198         }
5199
5200         for (l = m; l > i; l--)
5201           list->entry[l] = list->entry[l - 1];
5202       }
5203
5204       put_into_list:
5205
5206       *entry = *new_entry;
5207
5208       return i;
5209     }
5210     else if (one_score_entry_per_name &&
5211              strEqual(entry->name, new_entry->name))
5212     {
5213       // player already in high score list with better score or time
5214
5215       return -1;
5216     }
5217   }
5218
5219   // special case: new score is beyond the last high score list position
5220   return MAX_SCORE_ENTRIES;
5221 }
5222
5223 void NewHighScore(int level_nr, boolean tape_saved)
5224 {
5225   struct ScoreEntry new_entry = {{ 0 }}; // (prevent warning from GCC bug 53119)
5226   boolean one_per_name = FALSE;
5227
5228   strncpy(new_entry.tape_basename, tape.score_tape_basename, MAX_FILENAME_LEN);
5229   strncpy(new_entry.name, setup.player_name, MAX_PLAYER_NAME_LEN);
5230
5231   new_entry.score = game.score_final;
5232   new_entry.time = game.score_time_final;
5233
5234   LoadScore(level_nr);
5235
5236   scores.last_added = addScoreEntry(&scores, &new_entry, one_per_name);
5237
5238   if (scores.last_added >= MAX_SCORE_ENTRIES)
5239   {
5240     scores.last_added = MAX_SCORE_ENTRIES - 1;
5241     scores.force_last_added = TRUE;
5242
5243     scores.entry[scores.last_added] = new_entry;
5244
5245     // store last added local score entry (before merging server scores)
5246     scores.last_added_local = scores.last_added;
5247
5248     return;
5249   }
5250
5251   if (scores.last_added < 0)
5252     return;
5253
5254   SaveScore(level_nr);
5255
5256   // store last added local score entry (before merging server scores)
5257   scores.last_added_local = scores.last_added;
5258
5259   if (!game.LevelSolved_SaveTape)
5260     return;
5261
5262   SaveScoreTape(level_nr);
5263
5264   if (setup.ask_for_using_api_server)
5265   {
5266     setup.use_api_server =
5267       Request("Upload your score and tape to the high score server?", REQ_ASK);
5268
5269     if (!setup.use_api_server)
5270       Request("Not using high score server! Use setup menu to enable again!",
5271               REQ_CONFIRM);
5272
5273     runtime.use_api_server = setup.use_api_server;
5274
5275     // after asking for using API server once, do not ask again
5276     setup.ask_for_using_api_server = FALSE;
5277
5278     SaveSetup_ServerSetup();
5279   }
5280
5281   SaveServerScore(level_nr, tape_saved);
5282 }
5283
5284 void MergeServerScore(void)
5285 {
5286   struct ScoreEntry last_added_entry;
5287   boolean one_per_name = FALSE;
5288   int i;
5289
5290   if (scores.last_added >= 0)
5291     last_added_entry = scores.entry[scores.last_added];
5292
5293   for (i = 0; i < server_scores.num_entries; i++)
5294   {
5295     int pos = addScoreEntry(&scores, &server_scores.entry[i], one_per_name);
5296
5297     if (pos >= 0 && pos <= scores.last_added)
5298       scores.last_added++;
5299   }
5300
5301   if (scores.last_added >= MAX_SCORE_ENTRIES)
5302   {
5303     scores.last_added = MAX_SCORE_ENTRIES - 1;
5304     scores.force_last_added = TRUE;
5305
5306     scores.entry[scores.last_added] = last_added_entry;
5307   }
5308 }
5309
5310 static int getElementMoveStepsizeExt(int x, int y, int direction)
5311 {
5312   int element = Tile[x][y];
5313   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5314   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5315   int horiz_move = (dx != 0);
5316   int sign = (horiz_move ? dx : dy);
5317   int step = sign * element_info[element].move_stepsize;
5318
5319   // special values for move stepsize for spring and things on conveyor belt
5320   if (horiz_move)
5321   {
5322     if (CAN_FALL(element) &&
5323         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Tile[x][y + 1]))
5324       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5325     else if (element == EL_SPRING)
5326       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5327   }
5328
5329   return step;
5330 }
5331
5332 static int getElementMoveStepsize(int x, int y)
5333 {
5334   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5335 }
5336
5337 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5338 {
5339   if (player->GfxAction != action || player->GfxDir != dir)
5340   {
5341     player->GfxAction = action;
5342     player->GfxDir = dir;
5343     player->Frame = 0;
5344     player->StepFrame = 0;
5345   }
5346 }
5347
5348 static void ResetGfxFrame(int x, int y)
5349 {
5350   // profiling showed that "autotest" spends 10~20% of its time in this function
5351   if (DrawingDeactivatedField())
5352     return;
5353
5354   int element = Tile[x][y];
5355   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5356
5357   if (graphic_info[graphic].anim_global_sync)
5358     GfxFrame[x][y] = FrameCounter;
5359   else if (graphic_info[graphic].anim_global_anim_sync)
5360     GfxFrame[x][y] = getGlobalAnimSyncFrame();
5361   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5362     GfxFrame[x][y] = CustomValue[x][y];
5363   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5364     GfxFrame[x][y] = element_info[element].collect_score;
5365   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5366     GfxFrame[x][y] = ChangeDelay[x][y];
5367 }
5368
5369 static void ResetGfxAnimation(int x, int y)
5370 {
5371   GfxAction[x][y] = ACTION_DEFAULT;
5372   GfxDir[x][y] = MovDir[x][y];
5373   GfxFrame[x][y] = 0;
5374
5375   ResetGfxFrame(x, y);
5376 }
5377
5378 static void ResetRandomAnimationValue(int x, int y)
5379 {
5380   GfxRandom[x][y] = INIT_GFX_RANDOM();
5381 }
5382
5383 static void InitMovingField(int x, int y, int direction)
5384 {
5385   int element = Tile[x][y];
5386   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5387   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5388   int newx = x + dx;
5389   int newy = y + dy;
5390   boolean is_moving_before, is_moving_after;
5391
5392   // check if element was/is moving or being moved before/after mode change
5393   is_moving_before = (WasJustMoving[x][y] != 0);
5394   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5395
5396   // reset animation only for moving elements which change direction of moving
5397   // or which just started or stopped moving
5398   // (else CEs with property "can move" / "not moving" are reset each frame)
5399   if (is_moving_before != is_moving_after ||
5400       direction != MovDir[x][y])
5401     ResetGfxAnimation(x, y);
5402
5403   MovDir[x][y] = direction;
5404   GfxDir[x][y] = direction;
5405
5406   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5407                      direction == MV_DOWN && CAN_FALL(element) ?
5408                      ACTION_FALLING : ACTION_MOVING);
5409
5410   // this is needed for CEs with property "can move" / "not moving"
5411
5412   if (is_moving_after)
5413   {
5414     if (Tile[newx][newy] == EL_EMPTY)
5415       Tile[newx][newy] = EL_BLOCKED;
5416
5417     MovDir[newx][newy] = MovDir[x][y];
5418
5419     CustomValue[newx][newy] = CustomValue[x][y];
5420
5421     GfxFrame[newx][newy] = GfxFrame[x][y];
5422     GfxRandom[newx][newy] = GfxRandom[x][y];
5423     GfxAction[newx][newy] = GfxAction[x][y];
5424     GfxDir[newx][newy] = GfxDir[x][y];
5425   }
5426 }
5427
5428 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5429 {
5430   int direction = MovDir[x][y];
5431   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5432   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5433
5434   *goes_to_x = newx;
5435   *goes_to_y = newy;
5436 }
5437
5438 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5439 {
5440   int direction = MovDir[x][y];
5441   int oldx = x + (direction & MV_LEFT ? +1 : direction & MV_RIGHT ? -1 : 0);
5442   int oldy = y + (direction & MV_UP   ? +1 : direction & MV_DOWN  ? -1 : 0);
5443
5444   *comes_from_x = oldx;
5445   *comes_from_y = oldy;
5446 }
5447
5448 static int MovingOrBlocked2Element(int x, int y)
5449 {
5450   int element = Tile[x][y];
5451
5452   if (element == EL_BLOCKED)
5453   {
5454     int oldx, oldy;
5455
5456     Blocked2Moving(x, y, &oldx, &oldy);
5457
5458     return Tile[oldx][oldy];
5459   }
5460
5461   return element;
5462 }
5463
5464 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5465 {
5466   // like MovingOrBlocked2Element(), but if element is moving
5467   // and (x, y) is the field the moving element is just leaving,
5468   // return EL_BLOCKED instead of the element value
5469   int element = Tile[x][y];
5470
5471   if (IS_MOVING(x, y))
5472   {
5473     if (element == EL_BLOCKED)
5474     {
5475       int oldx, oldy;
5476
5477       Blocked2Moving(x, y, &oldx, &oldy);
5478       return Tile[oldx][oldy];
5479     }
5480     else
5481       return EL_BLOCKED;
5482   }
5483   else
5484     return element;
5485 }
5486
5487 static void RemoveField(int x, int y)
5488 {
5489   Tile[x][y] = EL_EMPTY;
5490
5491   MovPos[x][y] = 0;
5492   MovDir[x][y] = 0;
5493   MovDelay[x][y] = 0;
5494
5495   CustomValue[x][y] = 0;
5496
5497   AmoebaNr[x][y] = 0;
5498   ChangeDelay[x][y] = 0;
5499   ChangePage[x][y] = -1;
5500   Pushed[x][y] = FALSE;
5501
5502   GfxElement[x][y] = EL_UNDEFINED;
5503   GfxAction[x][y] = ACTION_DEFAULT;
5504   GfxDir[x][y] = MV_NONE;
5505 }
5506
5507 static void RemoveMovingField(int x, int y)
5508 {
5509   int oldx = x, oldy = y, newx = x, newy = y;
5510   int element = Tile[x][y];
5511   int next_element = EL_UNDEFINED;
5512
5513   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5514     return;
5515
5516   if (IS_MOVING(x, y))
5517   {
5518     Moving2Blocked(x, y, &newx, &newy);
5519
5520     if (Tile[newx][newy] != EL_BLOCKED)
5521     {
5522       // element is moving, but target field is not free (blocked), but
5523       // already occupied by something different (example: acid pool);
5524       // in this case, only remove the moving field, but not the target
5525
5526       RemoveField(oldx, oldy);
5527
5528       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5529
5530       TEST_DrawLevelField(oldx, oldy);
5531
5532       return;
5533     }
5534   }
5535   else if (element == EL_BLOCKED)
5536   {
5537     Blocked2Moving(x, y, &oldx, &oldy);
5538     if (!IS_MOVING(oldx, oldy))
5539       return;
5540   }
5541
5542   if (element == EL_BLOCKED &&
5543       (Tile[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5544        Tile[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5545        Tile[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5546        Tile[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5547        Tile[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5548        Tile[oldx][oldy] == EL_AMOEBA_DROPPING))
5549     next_element = get_next_element(Tile[oldx][oldy]);
5550
5551   RemoveField(oldx, oldy);
5552   RemoveField(newx, newy);
5553
5554   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5555
5556   if (next_element != EL_UNDEFINED)
5557     Tile[oldx][oldy] = next_element;
5558
5559   TEST_DrawLevelField(oldx, oldy);
5560   TEST_DrawLevelField(newx, newy);
5561 }
5562
5563 void DrawDynamite(int x, int y)
5564 {
5565   int sx = SCREENX(x), sy = SCREENY(y);
5566   int graphic = el2img(Tile[x][y]);
5567   int frame;
5568
5569   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5570     return;
5571
5572   if (IS_WALKABLE_INSIDE(Back[x][y]))
5573     return;
5574
5575   if (Back[x][y])
5576     DrawLevelElement(x, y, Back[x][y]);
5577   else if (Store[x][y])
5578     DrawLevelElement(x, y, Store[x][y]);
5579   else if (game.use_masked_elements)
5580     DrawLevelElement(x, y, EL_EMPTY);
5581
5582   frame = getGraphicAnimationFrameXY(graphic, x, y);
5583
5584   if (Back[x][y] || Store[x][y] || game.use_masked_elements)
5585     DrawGraphicThruMask(sx, sy, graphic, frame);
5586   else
5587     DrawGraphic(sx, sy, graphic, frame);
5588 }
5589
5590 static void CheckDynamite(int x, int y)
5591 {
5592   if (MovDelay[x][y] != 0)      // dynamite is still waiting to explode
5593   {
5594     MovDelay[x][y]--;
5595
5596     if (MovDelay[x][y] != 0)
5597     {
5598       DrawDynamite(x, y);
5599       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5600
5601       return;
5602     }
5603   }
5604
5605   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5606
5607   Bang(x, y);
5608 }
5609
5610 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5611 {
5612   boolean num_checked_players = 0;
5613   int i;
5614
5615   for (i = 0; i < MAX_PLAYERS; i++)
5616   {
5617     if (stored_player[i].active)
5618     {
5619       int sx = stored_player[i].jx;
5620       int sy = stored_player[i].jy;
5621
5622       if (num_checked_players == 0)
5623       {
5624         *sx1 = *sx2 = sx;
5625         *sy1 = *sy2 = sy;
5626       }
5627       else
5628       {
5629         *sx1 = MIN(*sx1, sx);
5630         *sy1 = MIN(*sy1, sy);
5631         *sx2 = MAX(*sx2, sx);
5632         *sy2 = MAX(*sy2, sy);
5633       }
5634
5635       num_checked_players++;
5636     }
5637   }
5638 }
5639
5640 static boolean checkIfAllPlayersFitToScreen_RND(void)
5641 {
5642   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5643
5644   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5645
5646   return (sx2 - sx1 < SCR_FIELDX &&
5647           sy2 - sy1 < SCR_FIELDY);
5648 }
5649
5650 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5651 {
5652   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5653
5654   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5655
5656   *sx = (sx1 + sx2) / 2;
5657   *sy = (sy1 + sy2) / 2;
5658 }
5659
5660 static void DrawRelocateScreen(int old_x, int old_y, int x, int y,
5661                                boolean center_screen, boolean quick_relocation)
5662 {
5663   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5664   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5665   boolean no_delay = (tape.warp_forward);
5666   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5667   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5668   int new_scroll_x, new_scroll_y;
5669
5670   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5671   {
5672     // case 1: quick relocation inside visible screen (without scrolling)
5673
5674     RedrawPlayfield();
5675
5676     return;
5677   }
5678
5679   if (!level.shifted_relocation || center_screen)
5680   {
5681     // relocation _with_ centering of screen
5682
5683     new_scroll_x = SCROLL_POSITION_X(x);
5684     new_scroll_y = SCROLL_POSITION_Y(y);
5685   }
5686   else
5687   {
5688     // relocation _without_ centering of screen
5689
5690     // apply distance between old and new player position to scroll position
5691     int shifted_scroll_x = scroll_x + (x - old_x);
5692     int shifted_scroll_y = scroll_y + (y - old_y);
5693
5694     // make sure that shifted scroll position does not scroll beyond screen
5695     new_scroll_x = SCROLL_POSITION_X(shifted_scroll_x + MIDPOSX);
5696     new_scroll_y = SCROLL_POSITION_Y(shifted_scroll_y + MIDPOSY);
5697
5698     // special case for teleporting from one end of the playfield to the other
5699     // (this kludge prevents the destination area to be shifted by half a tile
5700     // against the source destination for even screen width or screen height;
5701     // probably most useful when used with high "game.forced_scroll_delay_value"
5702     // in combination with "game.forced_scroll_x" and "game.forced_scroll_y")
5703     if (quick_relocation)
5704     {
5705       if (EVEN(SCR_FIELDX))
5706       {
5707         // relocate (teleport) between left and right border (half or full)
5708         if (scroll_x == SBX_Left && new_scroll_x == SBX_Right - 1)
5709           new_scroll_x = SBX_Right;
5710         else if (scroll_x == SBX_Left + 1 && new_scroll_x == SBX_Right)
5711           new_scroll_x = SBX_Right - 1;
5712         else if (scroll_x == SBX_Right && new_scroll_x == SBX_Left + 1)
5713           new_scroll_x = SBX_Left;
5714         else if (scroll_x == SBX_Right - 1 && new_scroll_x == SBX_Left)
5715           new_scroll_x = SBX_Left + 1;
5716       }
5717
5718       if (EVEN(SCR_FIELDY))
5719       {
5720         // relocate (teleport) between top and bottom border (half or full)
5721         if (scroll_y == SBY_Upper && new_scroll_y == SBY_Lower - 1)
5722           new_scroll_y = SBY_Lower;
5723         else if (scroll_y == SBY_Upper + 1 && new_scroll_y == SBY_Lower)
5724           new_scroll_y = SBY_Lower - 1;
5725         else if (scroll_y == SBY_Lower && new_scroll_y == SBY_Upper + 1)
5726           new_scroll_y = SBY_Upper;
5727         else if (scroll_y == SBY_Lower - 1 && new_scroll_y == SBY_Upper)
5728           new_scroll_y = SBY_Upper + 1;
5729       }
5730     }
5731   }
5732
5733   if (quick_relocation)
5734   {
5735     // case 2: quick relocation (redraw without visible scrolling)
5736
5737     scroll_x = new_scroll_x;
5738     scroll_y = new_scroll_y;
5739
5740     RedrawPlayfield();
5741
5742     return;
5743   }
5744
5745   // case 3: visible relocation (with scrolling to new position)
5746
5747   ScrollScreen(NULL, SCROLL_GO_ON);     // scroll last frame to full tile
5748
5749   SetVideoFrameDelay(wait_delay_value);
5750
5751   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5752   {
5753     int dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5754     int dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5755
5756     if (dx == 0 && dy == 0)             // no scrolling needed at all
5757       break;
5758
5759     scroll_x -= dx;
5760     scroll_y -= dy;
5761
5762     // set values for horizontal/vertical screen scrolling (half tile size)
5763     int dir_x = (dx != 0 ? MV_HORIZONTAL : 0);
5764     int dir_y = (dy != 0 ? MV_VERTICAL   : 0);
5765     int pos_x = dx * TILEX / 2;
5766     int pos_y = dy * TILEY / 2;
5767     int fx = getFieldbufferOffsetX_RND(dir_x, pos_x);
5768     int fy = getFieldbufferOffsetY_RND(dir_y, pos_y);
5769
5770     ScrollLevel(dx, dy);
5771     DrawAllPlayers();
5772
5773     // scroll in two steps of half tile size to make things smoother
5774     BlitScreenToBitmapExt_RND(window, fx, fy);
5775
5776     // scroll second step to align at full tile size
5777     BlitScreenToBitmap(window);
5778   }
5779
5780   DrawAllPlayers();
5781   BackToFront();
5782
5783   SetVideoFrameDelay(frame_delay_value_old);
5784 }
5785
5786 static void RelocatePlayer(int jx, int jy, int el_player_raw)
5787 {
5788   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5789   int player_nr = GET_PLAYER_NR(el_player);
5790   struct PlayerInfo *player = &stored_player[player_nr];
5791   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5792   boolean no_delay = (tape.warp_forward);
5793   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5794   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5795   int old_jx = player->jx;
5796   int old_jy = player->jy;
5797   int old_element = Tile[old_jx][old_jy];
5798   int element = Tile[jx][jy];
5799   boolean player_relocated = (old_jx != jx || old_jy != jy);
5800
5801   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5802   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5803   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5804   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5805   int leave_side_horiz = move_dir_horiz;
5806   int leave_side_vert  = move_dir_vert;
5807   int enter_side = enter_side_horiz | enter_side_vert;
5808   int leave_side = leave_side_horiz | leave_side_vert;
5809
5810   if (player->buried)           // do not reanimate dead player
5811     return;
5812
5813   if (!player_relocated)        // no need to relocate the player
5814     return;
5815
5816   if (IS_PLAYER(jx, jy))        // player already placed at new position
5817   {
5818     RemoveField(jx, jy);        // temporarily remove newly placed player
5819     DrawLevelField(jx, jy);
5820   }
5821
5822   if (player->present)
5823   {
5824     while (player->MovPos)
5825     {
5826       ScrollPlayer(player, SCROLL_GO_ON);
5827       ScrollScreen(NULL, SCROLL_GO_ON);
5828
5829       AdvanceFrameAndPlayerCounters(player->index_nr);
5830
5831       DrawPlayer(player);
5832
5833       BackToFront_WithFrameDelay(wait_delay_value);
5834     }
5835
5836     DrawPlayer(player);         // needed here only to cleanup last field
5837     DrawLevelField(player->jx, player->jy);     // remove player graphic
5838
5839     player->is_moving = FALSE;
5840   }
5841
5842   if (IS_CUSTOM_ELEMENT(old_element))
5843     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5844                                CE_LEFT_BY_PLAYER,
5845                                player->index_bit, leave_side);
5846
5847   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5848                                       CE_PLAYER_LEAVES_X,
5849                                       player->index_bit, leave_side);
5850
5851   Tile[jx][jy] = el_player;
5852   InitPlayerField(jx, jy, el_player, TRUE);
5853
5854   /* "InitPlayerField()" above sets Tile[jx][jy] to EL_EMPTY, but it may be
5855      possible that the relocation target field did not contain a player element,
5856      but a walkable element, to which the new player was relocated -- in this
5857      case, restore that (already initialized!) element on the player field */
5858   if (!IS_PLAYER_ELEMENT(element))      // player may be set on walkable element
5859   {
5860     Tile[jx][jy] = element;     // restore previously existing element
5861   }
5862
5863   // only visually relocate centered player
5864   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy,
5865                      FALSE, level.instant_relocation);
5866
5867   TestIfPlayerTouchesBadThing(jx, jy);
5868   TestIfPlayerTouchesCustomElement(jx, jy);
5869
5870   if (IS_CUSTOM_ELEMENT(element))
5871     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5872                                player->index_bit, enter_side);
5873
5874   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5875                                       player->index_bit, enter_side);
5876
5877   if (player->is_switching)
5878   {
5879     /* ensure that relocation while still switching an element does not cause
5880        a new element to be treated as also switched directly after relocation
5881        (this is important for teleporter switches that teleport the player to
5882        a place where another teleporter switch is in the same direction, which
5883        would then incorrectly be treated as immediately switched before the
5884        direction key that caused the switch was released) */
5885
5886     player->switch_x += jx - old_jx;
5887     player->switch_y += jy - old_jy;
5888   }
5889 }
5890
5891 static void Explode(int ex, int ey, int phase, int mode)
5892 {
5893   int x, y;
5894   int last_phase;
5895   int border_element;
5896
5897   if (game.explosions_delayed)
5898   {
5899     ExplodeField[ex][ey] = mode;
5900     return;
5901   }
5902
5903   if (phase == EX_PHASE_START)          // initialize 'Store[][]' field
5904   {
5905     int center_element = Tile[ex][ey];
5906     int ce_value = CustomValue[ex][ey];
5907     int ce_score = element_info[center_element].collect_score;
5908     int artwork_element, explosion_element;     // set these values later
5909
5910     // remove things displayed in background while burning dynamite
5911     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5912       Back[ex][ey] = 0;
5913
5914     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5915     {
5916       // put moving element to center field (and let it explode there)
5917       center_element = MovingOrBlocked2Element(ex, ey);
5918       RemoveMovingField(ex, ey);
5919       Tile[ex][ey] = center_element;
5920     }
5921
5922     // now "center_element" is finally determined -- set related values now
5923     artwork_element = center_element;           // for custom player artwork
5924     explosion_element = center_element;         // for custom player artwork
5925
5926     if (IS_PLAYER(ex, ey))
5927     {
5928       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5929
5930       artwork_element = stored_player[player_nr].artwork_element;
5931
5932       if (level.use_explosion_element[player_nr])
5933       {
5934         explosion_element = level.explosion_element[player_nr];
5935         artwork_element = explosion_element;
5936       }
5937     }
5938
5939     if (mode == EX_TYPE_NORMAL ||
5940         mode == EX_TYPE_CENTER ||
5941         mode == EX_TYPE_CROSS)
5942       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5943
5944     last_phase = element_info[explosion_element].explosion_delay + 1;
5945
5946     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5947     {
5948       int xx = x - ex + 1;
5949       int yy = y - ey + 1;
5950       int element;
5951
5952       if (!IN_LEV_FIELD(x, y) ||
5953           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5954           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5955         continue;
5956
5957       element = Tile[x][y];
5958
5959       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5960       {
5961         element = MovingOrBlocked2Element(x, y);
5962
5963         if (!IS_EXPLOSION_PROOF(element))
5964           RemoveMovingField(x, y);
5965       }
5966
5967       // indestructible elements can only explode in center (but not flames)
5968       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5969                                            mode == EX_TYPE_BORDER)) ||
5970           element == EL_FLAMES)
5971         continue;
5972
5973       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5974          behaviour, for example when touching a yamyam that explodes to rocks
5975          with active deadly shield, a rock is created under the player !!! */
5976       // (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8)
5977 #if 0
5978       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5979           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5980            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5981 #else
5982       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5983 #endif
5984       {
5985         if (IS_ACTIVE_BOMB(element))
5986         {
5987           // re-activate things under the bomb like gate or penguin
5988           Tile[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5989           Back[x][y] = 0;
5990         }
5991
5992         continue;
5993       }
5994
5995       // save walkable background elements while explosion on same tile
5996       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5997           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5998         Back[x][y] = element;
5999
6000       // ignite explodable elements reached by other explosion
6001       if (element == EL_EXPLOSION)
6002         element = Store2[x][y];
6003
6004       if (AmoebaNr[x][y] &&
6005           (element == EL_AMOEBA_FULL ||
6006            element == EL_BD_AMOEBA ||
6007            element == EL_AMOEBA_GROWING))
6008       {
6009         AmoebaCnt[AmoebaNr[x][y]]--;
6010         AmoebaCnt2[AmoebaNr[x][y]]--;
6011       }
6012
6013       RemoveField(x, y);
6014
6015       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
6016       {
6017         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
6018
6019         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
6020
6021         if (PLAYERINFO(ex, ey)->use_murphy)
6022           Store[x][y] = EL_EMPTY;
6023       }
6024
6025       // !!! check this case -- currently needed for rnd_rado_negundo_v,
6026       // !!! levels 015 018 019 020 021 022 023 026 027 028 !!!
6027       else if (IS_PLAYER_ELEMENT(center_element))
6028         Store[x][y] = EL_EMPTY;
6029       else if (center_element == EL_YAMYAM)
6030         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
6031       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
6032         Store[x][y] = element_info[center_element].content.e[xx][yy];
6033 #if 1
6034       // needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
6035       // (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
6036       // otherwise) -- FIX THIS !!!
6037       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
6038         Store[x][y] = element_info[element].content.e[1][1];
6039 #else
6040       else if (!CAN_EXPLODE(element))
6041         Store[x][y] = element_info[element].content.e[1][1];
6042 #endif
6043       else
6044         Store[x][y] = EL_EMPTY;
6045
6046       if (IS_CUSTOM_ELEMENT(center_element))
6047         Store[x][y] = (Store[x][y] == EL_CURRENT_CE_VALUE ? ce_value :
6048                        Store[x][y] == EL_CURRENT_CE_SCORE ? ce_score :
6049                        Store[x][y] >= EL_PREV_CE_8 &&
6050                        Store[x][y] <= EL_NEXT_CE_8 ?
6051                        RESOLVED_REFERENCE_ELEMENT(center_element, Store[x][y]) :
6052                        Store[x][y]);
6053
6054       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
6055           center_element == EL_AMOEBA_TO_DIAMOND)
6056         Store2[x][y] = element;
6057
6058       Tile[x][y] = EL_EXPLOSION;
6059       GfxElement[x][y] = artwork_element;
6060
6061       ExplodePhase[x][y] = 1;
6062       ExplodeDelay[x][y] = last_phase;
6063
6064       Stop[x][y] = TRUE;
6065     }
6066
6067     if (center_element == EL_YAMYAM)
6068       game.yamyam_content_nr =
6069         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
6070
6071     return;
6072   }
6073
6074   if (Stop[ex][ey])
6075     return;
6076
6077   x = ex;
6078   y = ey;
6079
6080   if (phase == 1)
6081     GfxFrame[x][y] = 0;         // restart explosion animation
6082
6083   last_phase = ExplodeDelay[x][y];
6084
6085   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
6086
6087   // this can happen if the player leaves an explosion just in time
6088   if (GfxElement[x][y] == EL_UNDEFINED)
6089     GfxElement[x][y] = EL_EMPTY;
6090
6091   border_element = Store2[x][y];
6092   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6093     border_element = StorePlayer[x][y];
6094
6095   if (phase == element_info[border_element].ignition_delay ||
6096       phase == last_phase)
6097   {
6098     boolean border_explosion = FALSE;
6099
6100     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
6101         !PLAYER_EXPLOSION_PROTECTED(x, y))
6102     {
6103       KillPlayerUnlessExplosionProtected(x, y);
6104       border_explosion = TRUE;
6105     }
6106     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
6107     {
6108       Tile[x][y] = Store2[x][y];
6109       Store2[x][y] = 0;
6110       Bang(x, y);
6111       border_explosion = TRUE;
6112     }
6113     else if (border_element == EL_AMOEBA_TO_DIAMOND)
6114     {
6115       AmoebaToDiamond(x, y);
6116       Store2[x][y] = 0;
6117       border_explosion = TRUE;
6118     }
6119
6120     // if an element just explodes due to another explosion (chain-reaction),
6121     // do not immediately end the new explosion when it was the last frame of
6122     // the explosion (as it would be done in the following "if"-statement!)
6123     if (border_explosion && phase == last_phase)
6124       return;
6125   }
6126
6127   // this can happen if the player was just killed by an explosion
6128   if (GfxElement[x][y] == EL_UNDEFINED)
6129     GfxElement[x][y] = EL_EMPTY;
6130
6131   if (phase == last_phase)
6132   {
6133     int element;
6134
6135     element = Tile[x][y] = Store[x][y];
6136     Store[x][y] = Store2[x][y] = 0;
6137     GfxElement[x][y] = EL_UNDEFINED;
6138
6139     // player can escape from explosions and might therefore be still alive
6140     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
6141         element <= EL_PLAYER_IS_EXPLODING_4)
6142     {
6143       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
6144       int explosion_element = EL_PLAYER_1 + player_nr;
6145       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
6146       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
6147
6148       if (level.use_explosion_element[player_nr])
6149         explosion_element = level.explosion_element[player_nr];
6150
6151       Tile[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
6152                     element_info[explosion_element].content.e[xx][yy]);
6153     }
6154
6155     // restore probably existing indestructible background element
6156     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
6157       element = Tile[x][y] = Back[x][y];
6158     Back[x][y] = 0;
6159
6160     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
6161     GfxDir[x][y] = MV_NONE;
6162     ChangeDelay[x][y] = 0;
6163     ChangePage[x][y] = -1;
6164
6165     CustomValue[x][y] = 0;
6166
6167     InitField_WithBug2(x, y, FALSE);
6168
6169     TEST_DrawLevelField(x, y);
6170
6171     TestIfElementTouchesCustomElement(x, y);
6172
6173     if (GFX_CRUMBLED(element))
6174       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6175
6176     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
6177       StorePlayer[x][y] = 0;
6178
6179     if (IS_PLAYER_ELEMENT(element))
6180       RelocatePlayer(x, y, element);
6181   }
6182   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6183   {
6184     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
6185     int frame = getGraphicAnimationFrameXY(graphic, x, y);
6186
6187     if (phase == 1)
6188       TEST_DrawLevelFieldCrumbled(x, y);
6189
6190     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
6191     {
6192       DrawLevelElement(x, y, Back[x][y]);
6193       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
6194     }
6195     else if (IS_WALKABLE_UNDER(Back[x][y]))
6196     {
6197       DrawLevelGraphic(x, y, graphic, frame);
6198       DrawLevelElementThruMask(x, y, Back[x][y]);
6199     }
6200     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
6201       DrawLevelGraphic(x, y, graphic, frame);
6202   }
6203 }
6204
6205 static void DynaExplode(int ex, int ey)
6206 {
6207   int i, j;
6208   int dynabomb_element = Tile[ex][ey];
6209   int dynabomb_size = 1;
6210   boolean dynabomb_xl = FALSE;
6211   struct PlayerInfo *player;
6212   struct XY *xy = xy_topdown;
6213
6214   if (IS_ACTIVE_BOMB(dynabomb_element))
6215   {
6216     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
6217     dynabomb_size = player->dynabomb_size;
6218     dynabomb_xl = player->dynabomb_xl;
6219     player->dynabombs_left++;
6220   }
6221
6222   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6223
6224   for (i = 0; i < NUM_DIRECTIONS; i++)
6225   {
6226     for (j = 1; j <= dynabomb_size; j++)
6227     {
6228       int x = ex + j * xy[i].x;
6229       int y = ey + j * xy[i].y;
6230       int element;
6231
6232       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Tile[x][y]))
6233         break;
6234
6235       element = Tile[x][y];
6236
6237       // do not restart explosions of fields with active bombs
6238       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6239         continue;
6240
6241       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6242
6243       if (element != EL_EMPTY && element != EL_EXPLOSION &&
6244           !IS_DIGGABLE(element) && !dynabomb_xl)
6245         break;
6246     }
6247   }
6248 }
6249
6250 void Bang(int x, int y)
6251 {
6252   int element = MovingOrBlocked2Element(x, y);
6253   int explosion_type = EX_TYPE_NORMAL;
6254
6255   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6256   {
6257     struct PlayerInfo *player = PLAYERINFO(x, y);
6258
6259     element = Tile[x][y] = player->initial_element;
6260
6261     if (level.use_explosion_element[player->index_nr])
6262     {
6263       int explosion_element = level.explosion_element[player->index_nr];
6264
6265       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6266         explosion_type = EX_TYPE_CROSS;
6267       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6268         explosion_type = EX_TYPE_CENTER;
6269     }
6270   }
6271
6272   switch (element)
6273   {
6274     case EL_BUG:
6275     case EL_SPACESHIP:
6276     case EL_BD_BUTTERFLY:
6277     case EL_BD_FIREFLY:
6278     case EL_YAMYAM:
6279     case EL_DARK_YAMYAM:
6280     case EL_ROBOT:
6281     case EL_PACMAN:
6282     case EL_MOLE:
6283       RaiseScoreElement(element);
6284       break;
6285
6286     case EL_DYNABOMB_PLAYER_1_ACTIVE:
6287     case EL_DYNABOMB_PLAYER_2_ACTIVE:
6288     case EL_DYNABOMB_PLAYER_3_ACTIVE:
6289     case EL_DYNABOMB_PLAYER_4_ACTIVE:
6290     case EL_DYNABOMB_INCREASE_NUMBER:
6291     case EL_DYNABOMB_INCREASE_SIZE:
6292     case EL_DYNABOMB_INCREASE_POWER:
6293       explosion_type = EX_TYPE_DYNA;
6294       break;
6295
6296     case EL_DC_LANDMINE:
6297       explosion_type = EX_TYPE_CENTER;
6298       break;
6299
6300     case EL_PENGUIN:
6301     case EL_LAMP:
6302     case EL_LAMP_ACTIVE:
6303     case EL_AMOEBA_TO_DIAMOND:
6304       if (!IS_PLAYER(x, y))     // penguin and player may be at same field
6305         explosion_type = EX_TYPE_CENTER;
6306       break;
6307
6308     default:
6309       if (element_info[element].explosion_type == EXPLODES_CROSS)
6310         explosion_type = EX_TYPE_CROSS;
6311       else if (element_info[element].explosion_type == EXPLODES_1X1)
6312         explosion_type = EX_TYPE_CENTER;
6313       break;
6314   }
6315
6316   if (explosion_type == EX_TYPE_DYNA)
6317     DynaExplode(x, y);
6318   else
6319     Explode(x, y, EX_PHASE_START, explosion_type);
6320
6321   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6322 }
6323
6324 static void SplashAcid(int x, int y)
6325 {
6326   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6327       (!IN_LEV_FIELD(x - 1, y - 2) ||
6328        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6329     Tile[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
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_RIGHT;
6335
6336   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6337 }
6338
6339 static void InitBeltMovement(void)
6340 {
6341   static int belt_base_element[4] =
6342   {
6343     EL_CONVEYOR_BELT_1_LEFT,
6344     EL_CONVEYOR_BELT_2_LEFT,
6345     EL_CONVEYOR_BELT_3_LEFT,
6346     EL_CONVEYOR_BELT_4_LEFT
6347   };
6348   static int belt_base_active_element[4] =
6349   {
6350     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6351     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6352     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6353     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6354   };
6355
6356   int x, y, i, j;
6357
6358   // set frame order for belt animation graphic according to belt direction
6359   for (i = 0; i < NUM_BELTS; i++)
6360   {
6361     int belt_nr = i;
6362
6363     for (j = 0; j < NUM_BELT_PARTS; j++)
6364     {
6365       int element = belt_base_active_element[belt_nr] + j;
6366       int graphic_1 = el2img(element);
6367       int graphic_2 = el2panelimg(element);
6368
6369       if (game.belt_dir[i] == MV_LEFT)
6370       {
6371         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6372         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6373       }
6374       else
6375       {
6376         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6377         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6378       }
6379     }
6380   }
6381
6382   SCAN_PLAYFIELD(x, y)
6383   {
6384     int element = Tile[x][y];
6385
6386     for (i = 0; i < NUM_BELTS; i++)
6387     {
6388       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6389       {
6390         int e_belt_nr = getBeltNrFromBeltElement(element);
6391         int belt_nr = i;
6392
6393         if (e_belt_nr == belt_nr)
6394         {
6395           int belt_part = Tile[x][y] - belt_base_element[belt_nr];
6396
6397           Tile[x][y] = belt_base_active_element[belt_nr] + belt_part;
6398         }
6399       }
6400     }
6401   }
6402 }
6403
6404 static void ToggleBeltSwitch(int x, int y)
6405 {
6406   static int belt_base_element[4] =
6407   {
6408     EL_CONVEYOR_BELT_1_LEFT,
6409     EL_CONVEYOR_BELT_2_LEFT,
6410     EL_CONVEYOR_BELT_3_LEFT,
6411     EL_CONVEYOR_BELT_4_LEFT
6412   };
6413   static int belt_base_active_element[4] =
6414   {
6415     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6416     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6417     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6418     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6419   };
6420   static int belt_base_switch_element[4] =
6421   {
6422     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6423     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6424     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6425     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6426   };
6427   static int belt_move_dir[4] =
6428   {
6429     MV_LEFT,
6430     MV_NONE,
6431     MV_RIGHT,
6432     MV_NONE,
6433   };
6434
6435   int element = Tile[x][y];
6436   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6437   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6438   int belt_dir = belt_move_dir[belt_dir_nr];
6439   int xx, yy, i;
6440
6441   if (!IS_BELT_SWITCH(element))
6442     return;
6443
6444   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6445   game.belt_dir[belt_nr] = belt_dir;
6446
6447   if (belt_dir_nr == 3)
6448     belt_dir_nr = 1;
6449
6450   // set frame order for belt animation graphic according to belt direction
6451   for (i = 0; i < NUM_BELT_PARTS; i++)
6452   {
6453     int element = belt_base_active_element[belt_nr] + i;
6454     int graphic_1 = el2img(element);
6455     int graphic_2 = el2panelimg(element);
6456
6457     if (belt_dir == MV_LEFT)
6458     {
6459       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6460       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6461     }
6462     else
6463     {
6464       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6465       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6466     }
6467   }
6468
6469   SCAN_PLAYFIELD(xx, yy)
6470   {
6471     int element = Tile[xx][yy];
6472
6473     if (IS_BELT_SWITCH(element))
6474     {
6475       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6476
6477       if (e_belt_nr == belt_nr)
6478       {
6479         Tile[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6480         TEST_DrawLevelField(xx, yy);
6481       }
6482     }
6483     else if (IS_BELT(element) && belt_dir != MV_NONE)
6484     {
6485       int e_belt_nr = getBeltNrFromBeltElement(element);
6486
6487       if (e_belt_nr == belt_nr)
6488       {
6489         int belt_part = Tile[xx][yy] - belt_base_element[belt_nr];
6490
6491         Tile[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6492         TEST_DrawLevelField(xx, yy);
6493       }
6494     }
6495     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6496     {
6497       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6498
6499       if (e_belt_nr == belt_nr)
6500       {
6501         int belt_part = Tile[xx][yy] - belt_base_active_element[belt_nr];
6502
6503         Tile[xx][yy] = belt_base_element[belt_nr] + belt_part;
6504         TEST_DrawLevelField(xx, yy);
6505       }
6506     }
6507   }
6508 }
6509
6510 static void ToggleSwitchgateSwitch(void)
6511 {
6512   int xx, yy;
6513
6514   game.switchgate_pos = !game.switchgate_pos;
6515
6516   SCAN_PLAYFIELD(xx, yy)
6517   {
6518     int element = Tile[xx][yy];
6519
6520     if (element == EL_SWITCHGATE_SWITCH_UP)
6521     {
6522       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6523       TEST_DrawLevelField(xx, yy);
6524     }
6525     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6526     {
6527       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6528       TEST_DrawLevelField(xx, yy);
6529     }
6530     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6531     {
6532       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6533       TEST_DrawLevelField(xx, yy);
6534     }
6535     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6536     {
6537       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6538       TEST_DrawLevelField(xx, yy);
6539     }
6540     else if (element == EL_SWITCHGATE_OPEN ||
6541              element == EL_SWITCHGATE_OPENING)
6542     {
6543       Tile[xx][yy] = EL_SWITCHGATE_CLOSING;
6544
6545       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6546     }
6547     else if (element == EL_SWITCHGATE_CLOSED ||
6548              element == EL_SWITCHGATE_CLOSING)
6549     {
6550       Tile[xx][yy] = EL_SWITCHGATE_OPENING;
6551
6552       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6553     }
6554   }
6555 }
6556
6557 static int getInvisibleActiveFromInvisibleElement(int element)
6558 {
6559   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6560           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6561           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6562           element);
6563 }
6564
6565 static int getInvisibleFromInvisibleActiveElement(int element)
6566 {
6567   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6568           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6569           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6570           element);
6571 }
6572
6573 static void RedrawAllLightSwitchesAndInvisibleElements(void)
6574 {
6575   int x, y;
6576
6577   SCAN_PLAYFIELD(x, y)
6578   {
6579     int element = Tile[x][y];
6580
6581     if (element == EL_LIGHT_SWITCH &&
6582         game.light_time_left > 0)
6583     {
6584       Tile[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6585       TEST_DrawLevelField(x, y);
6586     }
6587     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6588              game.light_time_left == 0)
6589     {
6590       Tile[x][y] = EL_LIGHT_SWITCH;
6591       TEST_DrawLevelField(x, y);
6592     }
6593     else if (element == EL_EMC_DRIPPER &&
6594              game.light_time_left > 0)
6595     {
6596       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6597       TEST_DrawLevelField(x, y);
6598     }
6599     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6600              game.light_time_left == 0)
6601     {
6602       Tile[x][y] = EL_EMC_DRIPPER;
6603       TEST_DrawLevelField(x, y);
6604     }
6605     else if (element == EL_INVISIBLE_STEELWALL ||
6606              element == EL_INVISIBLE_WALL ||
6607              element == EL_INVISIBLE_SAND)
6608     {
6609       if (game.light_time_left > 0)
6610         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6611
6612       TEST_DrawLevelField(x, y);
6613
6614       // uncrumble neighbour fields, if needed
6615       if (element == EL_INVISIBLE_SAND)
6616         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6617     }
6618     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6619              element == EL_INVISIBLE_WALL_ACTIVE ||
6620              element == EL_INVISIBLE_SAND_ACTIVE)
6621     {
6622       if (game.light_time_left == 0)
6623         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6624
6625       TEST_DrawLevelField(x, y);
6626
6627       // re-crumble neighbour fields, if needed
6628       if (element == EL_INVISIBLE_SAND)
6629         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6630     }
6631   }
6632 }
6633
6634 static void RedrawAllInvisibleElementsForLenses(void)
6635 {
6636   int x, y;
6637
6638   SCAN_PLAYFIELD(x, y)
6639   {
6640     int element = Tile[x][y];
6641
6642     if (element == EL_EMC_DRIPPER &&
6643         game.lenses_time_left > 0)
6644     {
6645       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6646       TEST_DrawLevelField(x, y);
6647     }
6648     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6649              game.lenses_time_left == 0)
6650     {
6651       Tile[x][y] = EL_EMC_DRIPPER;
6652       TEST_DrawLevelField(x, y);
6653     }
6654     else if (element == EL_INVISIBLE_STEELWALL ||
6655              element == EL_INVISIBLE_WALL ||
6656              element == EL_INVISIBLE_SAND)
6657     {
6658       if (game.lenses_time_left > 0)
6659         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6660
6661       TEST_DrawLevelField(x, y);
6662
6663       // uncrumble neighbour fields, if needed
6664       if (element == EL_INVISIBLE_SAND)
6665         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6666     }
6667     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6668              element == EL_INVISIBLE_WALL_ACTIVE ||
6669              element == EL_INVISIBLE_SAND_ACTIVE)
6670     {
6671       if (game.lenses_time_left == 0)
6672         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6673
6674       TEST_DrawLevelField(x, y);
6675
6676       // re-crumble neighbour fields, if needed
6677       if (element == EL_INVISIBLE_SAND)
6678         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6679     }
6680   }
6681 }
6682
6683 static void RedrawAllInvisibleElementsForMagnifier(void)
6684 {
6685   int x, y;
6686
6687   SCAN_PLAYFIELD(x, y)
6688   {
6689     int element = Tile[x][y];
6690
6691     if (element == EL_EMC_FAKE_GRASS &&
6692         game.magnify_time_left > 0)
6693     {
6694       Tile[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6695       TEST_DrawLevelField(x, y);
6696     }
6697     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6698              game.magnify_time_left == 0)
6699     {
6700       Tile[x][y] = EL_EMC_FAKE_GRASS;
6701       TEST_DrawLevelField(x, y);
6702     }
6703     else if (IS_GATE_GRAY(element) &&
6704              game.magnify_time_left > 0)
6705     {
6706       Tile[x][y] = (IS_RND_GATE_GRAY(element) ?
6707                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6708                     IS_EM_GATE_GRAY(element) ?
6709                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6710                     IS_EMC_GATE_GRAY(element) ?
6711                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6712                     IS_DC_GATE_GRAY(element) ?
6713                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6714                     element);
6715       TEST_DrawLevelField(x, y);
6716     }
6717     else if (IS_GATE_GRAY_ACTIVE(element) &&
6718              game.magnify_time_left == 0)
6719     {
6720       Tile[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6721                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6722                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6723                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6724                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6725                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6726                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6727                     EL_DC_GATE_WHITE_GRAY :
6728                     element);
6729       TEST_DrawLevelField(x, y);
6730     }
6731   }
6732 }
6733
6734 static void ToggleLightSwitch(int x, int y)
6735 {
6736   int element = Tile[x][y];
6737
6738   game.light_time_left =
6739     (element == EL_LIGHT_SWITCH ?
6740      level.time_light * FRAMES_PER_SECOND : 0);
6741
6742   RedrawAllLightSwitchesAndInvisibleElements();
6743 }
6744
6745 static void ActivateTimegateSwitch(int x, int y)
6746 {
6747   int xx, yy;
6748
6749   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6750
6751   SCAN_PLAYFIELD(xx, yy)
6752   {
6753     int element = Tile[xx][yy];
6754
6755     if (element == EL_TIMEGATE_CLOSED ||
6756         element == EL_TIMEGATE_CLOSING)
6757     {
6758       Tile[xx][yy] = EL_TIMEGATE_OPENING;
6759       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6760     }
6761
6762     /*
6763     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6764     {
6765       Tile[xx][yy] = EL_TIMEGATE_SWITCH;
6766       TEST_DrawLevelField(xx, yy);
6767     }
6768     */
6769
6770   }
6771
6772   Tile[x][y] = (Tile[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6773                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6774 }
6775
6776 static void Impact(int x, int y)
6777 {
6778   boolean last_line = (y == lev_fieldy - 1);
6779   boolean object_hit = FALSE;
6780   boolean impact = (last_line || object_hit);
6781   int element = Tile[x][y];
6782   int smashed = EL_STEELWALL;
6783
6784   if (!last_line)       // check if element below was hit
6785   {
6786     if (Tile[x][y + 1] == EL_PLAYER_IS_LEAVING)
6787       return;
6788
6789     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6790                                          MovDir[x][y + 1] != MV_DOWN ||
6791                                          MovPos[x][y + 1] <= TILEY / 2));
6792
6793     // do not smash moving elements that left the smashed field in time
6794     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6795         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6796       object_hit = FALSE;
6797
6798 #if USE_QUICKSAND_IMPACT_BUGFIX
6799     if (Tile[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6800     {
6801       RemoveMovingField(x, y + 1);
6802       Tile[x][y + 1] = EL_QUICKSAND_EMPTY;
6803       Tile[x][y + 2] = EL_ROCK;
6804       TEST_DrawLevelField(x, y + 2);
6805
6806       object_hit = TRUE;
6807     }
6808
6809     if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6810     {
6811       RemoveMovingField(x, y + 1);
6812       Tile[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6813       Tile[x][y + 2] = EL_ROCK;
6814       TEST_DrawLevelField(x, y + 2);
6815
6816       object_hit = TRUE;
6817     }
6818 #endif
6819
6820     if (object_hit)
6821       smashed = MovingOrBlocked2Element(x, y + 1);
6822
6823     impact = (last_line || object_hit);
6824   }
6825
6826   if (!last_line && smashed == EL_ACID) // element falls into acid
6827   {
6828     SplashAcid(x, y + 1);
6829     return;
6830   }
6831
6832   // !!! not sufficient for all cases -- see EL_PEARL below !!!
6833   // only reset graphic animation if graphic really changes after impact
6834   if (impact &&
6835       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6836   {
6837     ResetGfxAnimation(x, y);
6838     TEST_DrawLevelField(x, y);
6839   }
6840
6841   if (impact && CAN_EXPLODE_IMPACT(element))
6842   {
6843     Bang(x, y);
6844     return;
6845   }
6846   else if (impact && element == EL_PEARL &&
6847            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6848   {
6849     ResetGfxAnimation(x, y);
6850
6851     Tile[x][y] = EL_PEARL_BREAKING;
6852     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6853     return;
6854   }
6855   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6856   {
6857     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6858
6859     return;
6860   }
6861
6862   if (impact && element == EL_AMOEBA_DROP)
6863   {
6864     if (object_hit && IS_PLAYER(x, y + 1))
6865       KillPlayerUnlessEnemyProtected(x, y + 1);
6866     else if (object_hit && smashed == EL_PENGUIN)
6867       Bang(x, y + 1);
6868     else
6869     {
6870       Tile[x][y] = EL_AMOEBA_GROWING;
6871       Store[x][y] = EL_AMOEBA_WET;
6872
6873       ResetRandomAnimationValue(x, y);
6874     }
6875     return;
6876   }
6877
6878   if (object_hit)               // check which object was hit
6879   {
6880     if ((CAN_PASS_MAGIC_WALL(element) && 
6881          (smashed == EL_MAGIC_WALL ||
6882           smashed == EL_BD_MAGIC_WALL)) ||
6883         (CAN_PASS_DC_MAGIC_WALL(element) &&
6884          smashed == EL_DC_MAGIC_WALL))
6885     {
6886       int xx, yy;
6887       int activated_magic_wall =
6888         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6889          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6890          EL_DC_MAGIC_WALL_ACTIVE);
6891
6892       // activate magic wall / mill
6893       SCAN_PLAYFIELD(xx, yy)
6894       {
6895         if (Tile[xx][yy] == smashed)
6896           Tile[xx][yy] = activated_magic_wall;
6897       }
6898
6899       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6900       game.magic_wall_active = TRUE;
6901
6902       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6903                             SND_MAGIC_WALL_ACTIVATING :
6904                             smashed == EL_BD_MAGIC_WALL ?
6905                             SND_BD_MAGIC_WALL_ACTIVATING :
6906                             SND_DC_MAGIC_WALL_ACTIVATING));
6907     }
6908
6909     if (IS_PLAYER(x, y + 1))
6910     {
6911       if (CAN_SMASH_PLAYER(element))
6912       {
6913         KillPlayerUnlessEnemyProtected(x, y + 1);
6914         return;
6915       }
6916     }
6917     else if (smashed == EL_PENGUIN)
6918     {
6919       if (CAN_SMASH_PLAYER(element))
6920       {
6921         Bang(x, y + 1);
6922         return;
6923       }
6924     }
6925     else if (element == EL_BD_DIAMOND)
6926     {
6927       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6928       {
6929         Bang(x, y + 1);
6930         return;
6931       }
6932     }
6933     else if (((element == EL_SP_INFOTRON ||
6934                element == EL_SP_ZONK) &&
6935               (smashed == EL_SP_SNIKSNAK ||
6936                smashed == EL_SP_ELECTRON ||
6937                smashed == EL_SP_DISK_ORANGE)) ||
6938              (element == EL_SP_INFOTRON &&
6939               smashed == EL_SP_DISK_YELLOW))
6940     {
6941       Bang(x, y + 1);
6942       return;
6943     }
6944     else if (CAN_SMASH_EVERYTHING(element))
6945     {
6946       if (IS_CLASSIC_ENEMY(smashed) ||
6947           CAN_EXPLODE_SMASHED(smashed))
6948       {
6949         Bang(x, y + 1);
6950         return;
6951       }
6952       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6953       {
6954         if (smashed == EL_LAMP ||
6955             smashed == EL_LAMP_ACTIVE)
6956         {
6957           Bang(x, y + 1);
6958           return;
6959         }
6960         else if (smashed == EL_NUT)
6961         {
6962           Tile[x][y + 1] = EL_NUT_BREAKING;
6963           PlayLevelSound(x, y, SND_NUT_BREAKING);
6964           RaiseScoreElement(EL_NUT);
6965           return;
6966         }
6967         else if (smashed == EL_PEARL)
6968         {
6969           ResetGfxAnimation(x, y);
6970
6971           Tile[x][y + 1] = EL_PEARL_BREAKING;
6972           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6973           return;
6974         }
6975         else if (smashed == EL_DIAMOND)
6976         {
6977           Tile[x][y + 1] = EL_DIAMOND_BREAKING;
6978           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6979           return;
6980         }
6981         else if (IS_BELT_SWITCH(smashed))
6982         {
6983           ToggleBeltSwitch(x, y + 1);
6984         }
6985         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6986                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6987                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6988                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6989         {
6990           ToggleSwitchgateSwitch();
6991         }
6992         else if (smashed == EL_LIGHT_SWITCH ||
6993                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6994         {
6995           ToggleLightSwitch(x, y + 1);
6996         }
6997         else
6998         {
6999           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
7000
7001           CheckElementChangeBySide(x, y + 1, smashed, element,
7002                                    CE_SWITCHED, CH_SIDE_TOP);
7003           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
7004                                             CH_SIDE_TOP);
7005         }
7006       }
7007       else
7008       {
7009         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
7010       }
7011     }
7012   }
7013
7014   // play sound of magic wall / mill
7015   if (!last_line &&
7016       (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7017        Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
7018        Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
7019   {
7020     if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7021       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
7022     else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7023       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
7024     else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7025       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
7026
7027     return;
7028   }
7029
7030   // play sound of object that hits the ground
7031   if (last_line || object_hit)
7032     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
7033 }
7034
7035 static void TurnRoundExt(int x, int y)
7036 {
7037   static struct
7038   {
7039     int dx, dy;
7040   } move_xy[] =
7041   {
7042     {  0,  0 },
7043     { -1,  0 },
7044     { +1,  0 },
7045     {  0,  0 },
7046     {  0, -1 },
7047     {  0,  0 }, { 0, 0 }, { 0, 0 },
7048     {  0, +1 }
7049   };
7050   static struct
7051   {
7052     int left, right, back;
7053   } turn[] =
7054   {
7055     { 0,        0,              0        },
7056     { MV_DOWN,  MV_UP,          MV_RIGHT },
7057     { MV_UP,    MV_DOWN,        MV_LEFT  },
7058     { 0,        0,              0        },
7059     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
7060     { 0,        0,              0        },
7061     { 0,        0,              0        },
7062     { 0,        0,              0        },
7063     { MV_RIGHT, MV_LEFT,        MV_UP    }
7064   };
7065
7066   int element = Tile[x][y];
7067   int move_pattern = element_info[element].move_pattern;
7068
7069   int old_move_dir = MovDir[x][y];
7070   int left_dir  = turn[old_move_dir].left;
7071   int right_dir = turn[old_move_dir].right;
7072   int back_dir  = turn[old_move_dir].back;
7073
7074   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
7075   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
7076   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
7077   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
7078
7079   int left_x  = x + left_dx,  left_y  = y + left_dy;
7080   int right_x = x + right_dx, right_y = y + right_dy;
7081   int move_x  = x + move_dx,  move_y  = y + move_dy;
7082
7083   int xx, yy;
7084
7085   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
7086   {
7087     TestIfBadThingTouchesOtherBadThing(x, y);
7088
7089     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
7090       MovDir[x][y] = right_dir;
7091     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7092       MovDir[x][y] = left_dir;
7093
7094     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
7095       MovDelay[x][y] = 9;
7096     else if (element == EL_BD_BUTTERFLY)     // && MovDir[x][y] == left_dir)
7097       MovDelay[x][y] = 1;
7098   }
7099   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
7100   {
7101     TestIfBadThingTouchesOtherBadThing(x, y);
7102
7103     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
7104       MovDir[x][y] = left_dir;
7105     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7106       MovDir[x][y] = right_dir;
7107
7108     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
7109       MovDelay[x][y] = 9;
7110     else if (element == EL_BD_FIREFLY)      // && MovDir[x][y] == right_dir)
7111       MovDelay[x][y] = 1;
7112   }
7113   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
7114   {
7115     TestIfBadThingTouchesOtherBadThing(x, y);
7116
7117     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
7118       MovDir[x][y] = left_dir;
7119     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
7120       MovDir[x][y] = right_dir;
7121
7122     if (MovDir[x][y] != old_move_dir)
7123       MovDelay[x][y] = 9;
7124   }
7125   else if (element == EL_YAMYAM)
7126   {
7127     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
7128     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
7129
7130     if (can_turn_left && can_turn_right)
7131       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7132     else if (can_turn_left)
7133       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7134     else if (can_turn_right)
7135       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7136     else
7137       MovDir[x][y] = back_dir;
7138
7139     MovDelay[x][y] = 16 + 16 * RND(3);
7140   }
7141   else if (element == EL_DARK_YAMYAM)
7142   {
7143     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7144                                                          left_x, left_y);
7145     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7146                                                          right_x, right_y);
7147
7148     if (can_turn_left && can_turn_right)
7149       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7150     else if (can_turn_left)
7151       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7152     else if (can_turn_right)
7153       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7154     else
7155       MovDir[x][y] = back_dir;
7156
7157     MovDelay[x][y] = 16 + 16 * RND(3);
7158   }
7159   else if (element == EL_PACMAN)
7160   {
7161     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
7162     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
7163
7164     if (can_turn_left && can_turn_right)
7165       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7166     else if (can_turn_left)
7167       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7168     else if (can_turn_right)
7169       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7170     else
7171       MovDir[x][y] = back_dir;
7172
7173     MovDelay[x][y] = 6 + RND(40);
7174   }
7175   else if (element == EL_PIG)
7176   {
7177     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
7178     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
7179     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
7180     boolean should_turn_left, should_turn_right, should_move_on;
7181     int rnd_value = 24;
7182     int rnd = RND(rnd_value);
7183
7184     should_turn_left = (can_turn_left &&
7185                         (!can_move_on ||
7186                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
7187                                                    y + back_dy + left_dy)));
7188     should_turn_right = (can_turn_right &&
7189                          (!can_move_on ||
7190                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
7191                                                     y + back_dy + right_dy)));
7192     should_move_on = (can_move_on &&
7193                       (!can_turn_left ||
7194                        !can_turn_right ||
7195                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
7196                                                  y + move_dy + left_dy) ||
7197                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
7198                                                  y + move_dy + right_dy)));
7199
7200     if (should_turn_left || should_turn_right || should_move_on)
7201     {
7202       if (should_turn_left && should_turn_right && should_move_on)
7203         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
7204                         rnd < 2 * rnd_value / 3 ? right_dir :
7205                         old_move_dir);
7206       else if (should_turn_left && should_turn_right)
7207         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7208       else if (should_turn_left && should_move_on)
7209         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
7210       else if (should_turn_right && should_move_on)
7211         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
7212       else if (should_turn_left)
7213         MovDir[x][y] = left_dir;
7214       else if (should_turn_right)
7215         MovDir[x][y] = right_dir;
7216       else if (should_move_on)
7217         MovDir[x][y] = old_move_dir;
7218     }
7219     else if (can_move_on && rnd > rnd_value / 8)
7220       MovDir[x][y] = old_move_dir;
7221     else if (can_turn_left && can_turn_right)
7222       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7223     else if (can_turn_left && rnd > rnd_value / 8)
7224       MovDir[x][y] = left_dir;
7225     else if (can_turn_right && rnd > rnd_value/8)
7226       MovDir[x][y] = right_dir;
7227     else
7228       MovDir[x][y] = back_dir;
7229
7230     xx = x + move_xy[MovDir[x][y]].dx;
7231     yy = y + move_xy[MovDir[x][y]].dy;
7232
7233     if (!IN_LEV_FIELD(xx, yy) ||
7234         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Tile[xx][yy])))
7235       MovDir[x][y] = old_move_dir;
7236
7237     MovDelay[x][y] = 0;
7238   }
7239   else if (element == EL_DRAGON)
7240   {
7241     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7242     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7243     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7244     int rnd_value = 24;
7245     int rnd = RND(rnd_value);
7246
7247     if (can_move_on && rnd > rnd_value / 8)
7248       MovDir[x][y] = old_move_dir;
7249     else if (can_turn_left && can_turn_right)
7250       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7251     else if (can_turn_left && rnd > rnd_value / 8)
7252       MovDir[x][y] = left_dir;
7253     else if (can_turn_right && rnd > rnd_value / 8)
7254       MovDir[x][y] = right_dir;
7255     else
7256       MovDir[x][y] = back_dir;
7257
7258     xx = x + move_xy[MovDir[x][y]].dx;
7259     yy = y + move_xy[MovDir[x][y]].dy;
7260
7261     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7262       MovDir[x][y] = old_move_dir;
7263
7264     MovDelay[x][y] = 0;
7265   }
7266   else if (element == EL_MOLE)
7267   {
7268     boolean can_move_on =
7269       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7270                             IS_AMOEBOID(Tile[move_x][move_y]) ||
7271                             Tile[move_x][move_y] == EL_AMOEBA_SHRINKING));
7272     if (!can_move_on)
7273     {
7274       boolean can_turn_left =
7275         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7276                               IS_AMOEBOID(Tile[left_x][left_y])));
7277
7278       boolean can_turn_right =
7279         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7280                               IS_AMOEBOID(Tile[right_x][right_y])));
7281
7282       if (can_turn_left && can_turn_right)
7283         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7284       else if (can_turn_left)
7285         MovDir[x][y] = left_dir;
7286       else
7287         MovDir[x][y] = right_dir;
7288     }
7289
7290     if (MovDir[x][y] != old_move_dir)
7291       MovDelay[x][y] = 9;
7292   }
7293   else if (element == EL_BALLOON)
7294   {
7295     MovDir[x][y] = game.wind_direction;
7296     MovDelay[x][y] = 0;
7297   }
7298   else if (element == EL_SPRING)
7299   {
7300     if (MovDir[x][y] & MV_HORIZONTAL)
7301     {
7302       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7303           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7304       {
7305         Tile[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7306         ResetGfxAnimation(move_x, move_y);
7307         TEST_DrawLevelField(move_x, move_y);
7308
7309         MovDir[x][y] = back_dir;
7310       }
7311       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7312                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7313         MovDir[x][y] = MV_NONE;
7314     }
7315
7316     MovDelay[x][y] = 0;
7317   }
7318   else if (element == EL_ROBOT ||
7319            element == EL_SATELLITE ||
7320            element == EL_PENGUIN ||
7321            element == EL_EMC_ANDROID)
7322   {
7323     int attr_x = -1, attr_y = -1;
7324
7325     if (game.all_players_gone)
7326     {
7327       attr_x = game.exit_x;
7328       attr_y = game.exit_y;
7329     }
7330     else
7331     {
7332       int i;
7333
7334       for (i = 0; i < MAX_PLAYERS; i++)
7335       {
7336         struct PlayerInfo *player = &stored_player[i];
7337         int jx = player->jx, jy = player->jy;
7338
7339         if (!player->active)
7340           continue;
7341
7342         if (attr_x == -1 ||
7343             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7344         {
7345           attr_x = jx;
7346           attr_y = jy;
7347         }
7348       }
7349     }
7350
7351     if (element == EL_ROBOT &&
7352         game.robot_wheel_x >= 0 &&
7353         game.robot_wheel_y >= 0 &&
7354         (Tile[game.robot_wheel_x][game.robot_wheel_y] == EL_ROBOT_WHEEL_ACTIVE ||
7355          game.engine_version < VERSION_IDENT(3,1,0,0)))
7356     {
7357       attr_x = game.robot_wheel_x;
7358       attr_y = game.robot_wheel_y;
7359     }
7360
7361     if (element == EL_PENGUIN)
7362     {
7363       int i;
7364       struct XY *xy = xy_topdown;
7365
7366       for (i = 0; i < NUM_DIRECTIONS; i++)
7367       {
7368         int ex = x + xy[i].x;
7369         int ey = y + xy[i].y;
7370
7371         if (IN_LEV_FIELD(ex, ey) && (Tile[ex][ey] == EL_EXIT_OPEN ||
7372                                      Tile[ex][ey] == EL_EM_EXIT_OPEN ||
7373                                      Tile[ex][ey] == EL_STEEL_EXIT_OPEN ||
7374                                      Tile[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7375         {
7376           attr_x = ex;
7377           attr_y = ey;
7378           break;
7379         }
7380       }
7381     }
7382
7383     MovDir[x][y] = MV_NONE;
7384     if (attr_x < x)
7385       MovDir[x][y] |= (game.all_players_gone ? MV_RIGHT : MV_LEFT);
7386     else if (attr_x > x)
7387       MovDir[x][y] |= (game.all_players_gone ? MV_LEFT : MV_RIGHT);
7388     if (attr_y < y)
7389       MovDir[x][y] |= (game.all_players_gone ? MV_DOWN : MV_UP);
7390     else if (attr_y > y)
7391       MovDir[x][y] |= (game.all_players_gone ? MV_UP : MV_DOWN);
7392
7393     if (element == EL_ROBOT)
7394     {
7395       int newx, newy;
7396
7397       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7398         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7399       Moving2Blocked(x, y, &newx, &newy);
7400
7401       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7402         MovDelay[x][y] = 8 + 8 * !RND(3);
7403       else
7404         MovDelay[x][y] = 16;
7405     }
7406     else if (element == EL_PENGUIN)
7407     {
7408       int newx, newy;
7409
7410       MovDelay[x][y] = 1;
7411
7412       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7413       {
7414         boolean first_horiz = RND(2);
7415         int new_move_dir = MovDir[x][y];
7416
7417         MovDir[x][y] =
7418           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7419         Moving2Blocked(x, y, &newx, &newy);
7420
7421         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7422           return;
7423
7424         MovDir[x][y] =
7425           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7426         Moving2Blocked(x, y, &newx, &newy);
7427
7428         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7429           return;
7430
7431         MovDir[x][y] = old_move_dir;
7432         return;
7433       }
7434     }
7435     else if (element == EL_SATELLITE)
7436     {
7437       int newx, newy;
7438
7439       MovDelay[x][y] = 1;
7440
7441       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7442       {
7443         boolean first_horiz = RND(2);
7444         int new_move_dir = MovDir[x][y];
7445
7446         MovDir[x][y] =
7447           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7448         Moving2Blocked(x, y, &newx, &newy);
7449
7450         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7451           return;
7452
7453         MovDir[x][y] =
7454           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7455         Moving2Blocked(x, y, &newx, &newy);
7456
7457         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7458           return;
7459
7460         MovDir[x][y] = old_move_dir;
7461         return;
7462       }
7463     }
7464     else if (element == EL_EMC_ANDROID)
7465     {
7466       static int check_pos[16] =
7467       {
7468         -1,             //  0 => (invalid)
7469         7,              //  1 => MV_LEFT
7470         3,              //  2 => MV_RIGHT
7471         -1,             //  3 => (invalid)
7472         1,              //  4 =>            MV_UP
7473         0,              //  5 => MV_LEFT  | MV_UP
7474         2,              //  6 => MV_RIGHT | MV_UP
7475         -1,             //  7 => (invalid)
7476         5,              //  8 =>            MV_DOWN
7477         6,              //  9 => MV_LEFT  | MV_DOWN
7478         4,              // 10 => MV_RIGHT | MV_DOWN
7479         -1,             // 11 => (invalid)
7480         -1,             // 12 => (invalid)
7481         -1,             // 13 => (invalid)
7482         -1,             // 14 => (invalid)
7483         -1,             // 15 => (invalid)
7484       };
7485       static struct
7486       {
7487         int dx, dy;
7488         int dir;
7489       } check_xy[8] =
7490       {
7491         { -1, -1,       MV_LEFT  | MV_UP   },
7492         {  0, -1,                  MV_UP   },
7493         { +1, -1,       MV_RIGHT | MV_UP   },
7494         { +1,  0,       MV_RIGHT           },
7495         { +1, +1,       MV_RIGHT | MV_DOWN },
7496         {  0, +1,                  MV_DOWN },
7497         { -1, +1,       MV_LEFT  | MV_DOWN },
7498         { -1,  0,       MV_LEFT            },
7499       };
7500       int start_pos, check_order;
7501       boolean can_clone = FALSE;
7502       int i;
7503
7504       // check if there is any free field around current position
7505       for (i = 0; i < 8; i++)
7506       {
7507         int newx = x + check_xy[i].dx;
7508         int newy = y + check_xy[i].dy;
7509
7510         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7511         {
7512           can_clone = TRUE;
7513
7514           break;
7515         }
7516       }
7517
7518       if (can_clone)            // randomly find an element to clone
7519       {
7520         can_clone = FALSE;
7521
7522         start_pos = check_pos[RND(8)];
7523         check_order = (RND(2) ? -1 : +1);
7524
7525         for (i = 0; i < 8; i++)
7526         {
7527           int pos_raw = start_pos + i * check_order;
7528           int pos = (pos_raw + 8) % 8;
7529           int newx = x + check_xy[pos].dx;
7530           int newy = y + check_xy[pos].dy;
7531
7532           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7533           {
7534             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7535             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7536
7537             Store[x][y] = Tile[newx][newy];
7538
7539             can_clone = TRUE;
7540
7541             break;
7542           }
7543         }
7544       }
7545
7546       if (can_clone)            // randomly find a direction to move
7547       {
7548         can_clone = FALSE;
7549
7550         start_pos = check_pos[RND(8)];
7551         check_order = (RND(2) ? -1 : +1);
7552
7553         for (i = 0; i < 8; i++)
7554         {
7555           int pos_raw = start_pos + i * check_order;
7556           int pos = (pos_raw + 8) % 8;
7557           int newx = x + check_xy[pos].dx;
7558           int newy = y + check_xy[pos].dy;
7559           int new_move_dir = check_xy[pos].dir;
7560
7561           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7562           {
7563             MovDir[x][y] = new_move_dir;
7564             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7565
7566             can_clone = TRUE;
7567
7568             break;
7569           }
7570         }
7571       }
7572
7573       if (can_clone)            // cloning and moving successful
7574         return;
7575
7576       // cannot clone -- try to move towards player
7577
7578       start_pos = check_pos[MovDir[x][y] & 0x0f];
7579       check_order = (RND(2) ? -1 : +1);
7580
7581       for (i = 0; i < 3; i++)
7582       {
7583         // first check start_pos, then previous/next or (next/previous) pos
7584         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7585         int pos = (pos_raw + 8) % 8;
7586         int newx = x + check_xy[pos].dx;
7587         int newy = y + check_xy[pos].dy;
7588         int new_move_dir = check_xy[pos].dir;
7589
7590         if (IS_PLAYER(newx, newy))
7591           break;
7592
7593         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7594         {
7595           MovDir[x][y] = new_move_dir;
7596           MovDelay[x][y] = level.android_move_time * 8 + 1;
7597
7598           break;
7599         }
7600       }
7601     }
7602   }
7603   else if (move_pattern == MV_TURNING_LEFT ||
7604            move_pattern == MV_TURNING_RIGHT ||
7605            move_pattern == MV_TURNING_LEFT_RIGHT ||
7606            move_pattern == MV_TURNING_RIGHT_LEFT ||
7607            move_pattern == MV_TURNING_RANDOM ||
7608            move_pattern == MV_ALL_DIRECTIONS)
7609   {
7610     boolean can_turn_left =
7611       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7612     boolean can_turn_right =
7613       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y);
7614
7615     if (element_info[element].move_stepsize == 0)       // "not moving"
7616       return;
7617
7618     if (move_pattern == MV_TURNING_LEFT)
7619       MovDir[x][y] = left_dir;
7620     else if (move_pattern == MV_TURNING_RIGHT)
7621       MovDir[x][y] = right_dir;
7622     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7623       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7624     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7625       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7626     else if (move_pattern == MV_TURNING_RANDOM)
7627       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7628                       can_turn_right && !can_turn_left ? right_dir :
7629                       RND(2) ? left_dir : right_dir);
7630     else if (can_turn_left && can_turn_right)
7631       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7632     else if (can_turn_left)
7633       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7634     else if (can_turn_right)
7635       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7636     else
7637       MovDir[x][y] = back_dir;
7638
7639     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7640   }
7641   else if (move_pattern == MV_HORIZONTAL ||
7642            move_pattern == MV_VERTICAL)
7643   {
7644     if (move_pattern & old_move_dir)
7645       MovDir[x][y] = back_dir;
7646     else if (move_pattern == MV_HORIZONTAL)
7647       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7648     else if (move_pattern == MV_VERTICAL)
7649       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7650
7651     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7652   }
7653   else if (move_pattern & MV_ANY_DIRECTION)
7654   {
7655     MovDir[x][y] = move_pattern;
7656     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7657   }
7658   else if (move_pattern & MV_WIND_DIRECTION)
7659   {
7660     MovDir[x][y] = game.wind_direction;
7661     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7662   }
7663   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7664   {
7665     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7666       MovDir[x][y] = left_dir;
7667     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7668       MovDir[x][y] = right_dir;
7669
7670     if (MovDir[x][y] != old_move_dir)
7671       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7672   }
7673   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7674   {
7675     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7676       MovDir[x][y] = right_dir;
7677     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7678       MovDir[x][y] = left_dir;
7679
7680     if (MovDir[x][y] != old_move_dir)
7681       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7682   }
7683   else if (move_pattern == MV_TOWARDS_PLAYER ||
7684            move_pattern == MV_AWAY_FROM_PLAYER)
7685   {
7686     int attr_x = -1, attr_y = -1;
7687     int newx, newy;
7688     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7689
7690     if (game.all_players_gone)
7691     {
7692       attr_x = game.exit_x;
7693       attr_y = game.exit_y;
7694     }
7695     else
7696     {
7697       int i;
7698
7699       for (i = 0; i < MAX_PLAYERS; i++)
7700       {
7701         struct PlayerInfo *player = &stored_player[i];
7702         int jx = player->jx, jy = player->jy;
7703
7704         if (!player->active)
7705           continue;
7706
7707         if (attr_x == -1 ||
7708             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7709         {
7710           attr_x = jx;
7711           attr_y = jy;
7712         }
7713       }
7714     }
7715
7716     MovDir[x][y] = MV_NONE;
7717     if (attr_x < x)
7718       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7719     else if (attr_x > x)
7720       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7721     if (attr_y < y)
7722       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7723     else if (attr_y > y)
7724       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7725
7726     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7727
7728     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7729     {
7730       boolean first_horiz = RND(2);
7731       int new_move_dir = MovDir[x][y];
7732
7733       if (element_info[element].move_stepsize == 0)     // "not moving"
7734       {
7735         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7736         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7737
7738         return;
7739       }
7740
7741       MovDir[x][y] =
7742         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7743       Moving2Blocked(x, y, &newx, &newy);
7744
7745       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7746         return;
7747
7748       MovDir[x][y] =
7749         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7750       Moving2Blocked(x, y, &newx, &newy);
7751
7752       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7753         return;
7754
7755       MovDir[x][y] = old_move_dir;
7756     }
7757   }
7758   else if (move_pattern == MV_WHEN_PUSHED ||
7759            move_pattern == MV_WHEN_DROPPED)
7760   {
7761     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7762       MovDir[x][y] = MV_NONE;
7763
7764     MovDelay[x][y] = 0;
7765   }
7766   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7767   {
7768     struct XY *test_xy = xy_topdown;
7769     static int test_dir[4] =
7770     {
7771       MV_UP,
7772       MV_LEFT,
7773       MV_RIGHT,
7774       MV_DOWN
7775     };
7776     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7777     int move_preference = -1000000;     // start with very low preference
7778     int new_move_dir = MV_NONE;
7779     int start_test = RND(4);
7780     int i;
7781
7782     for (i = 0; i < NUM_DIRECTIONS; i++)
7783     {
7784       int j = (start_test + i) % 4;
7785       int move_dir = test_dir[j];
7786       int move_dir_preference;
7787
7788       xx = x + test_xy[j].x;
7789       yy = y + test_xy[j].y;
7790
7791       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7792           (IS_PLAYER(xx, yy) || Tile[xx][yy] == EL_PLAYER_IS_LEAVING))
7793       {
7794         new_move_dir = move_dir;
7795
7796         break;
7797       }
7798
7799       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7800         continue;
7801
7802       move_dir_preference = -1 * RunnerVisit[xx][yy];
7803       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7804         move_dir_preference = PlayerVisit[xx][yy];
7805
7806       if (move_dir_preference > move_preference)
7807       {
7808         // prefer field that has not been visited for the longest time
7809         move_preference = move_dir_preference;
7810         new_move_dir = move_dir;
7811       }
7812       else if (move_dir_preference == move_preference &&
7813                move_dir == old_move_dir)
7814       {
7815         // prefer last direction when all directions are preferred equally
7816         move_preference = move_dir_preference;
7817         new_move_dir = move_dir;
7818       }
7819     }
7820
7821     MovDir[x][y] = new_move_dir;
7822     if (old_move_dir != new_move_dir)
7823       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7824   }
7825 }
7826
7827 static void TurnRound(int x, int y)
7828 {
7829   int direction = MovDir[x][y];
7830
7831   TurnRoundExt(x, y);
7832
7833   GfxDir[x][y] = MovDir[x][y];
7834
7835   if (direction != MovDir[x][y])
7836     GfxFrame[x][y] = 0;
7837
7838   if (MovDelay[x][y])
7839     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7840
7841   ResetGfxFrame(x, y);
7842 }
7843
7844 static boolean JustBeingPushed(int x, int y)
7845 {
7846   int i;
7847
7848   for (i = 0; i < MAX_PLAYERS; i++)
7849   {
7850     struct PlayerInfo *player = &stored_player[i];
7851
7852     if (player->active && player->is_pushing && player->MovPos)
7853     {
7854       int next_jx = player->jx + (player->jx - player->last_jx);
7855       int next_jy = player->jy + (player->jy - player->last_jy);
7856
7857       if (x == next_jx && y == next_jy)
7858         return TRUE;
7859     }
7860   }
7861
7862   return FALSE;
7863 }
7864
7865 static void StartMoving(int x, int y)
7866 {
7867   boolean started_moving = FALSE;       // some elements can fall _and_ move
7868   int element = Tile[x][y];
7869
7870   if (Stop[x][y])
7871     return;
7872
7873   if (MovDelay[x][y] == 0)
7874     GfxAction[x][y] = ACTION_DEFAULT;
7875
7876   if (CAN_FALL(element) && y < lev_fieldy - 1)
7877   {
7878     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7879         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7880       if (JustBeingPushed(x, y))
7881         return;
7882
7883     if (element == EL_QUICKSAND_FULL)
7884     {
7885       if (IS_FREE(x, y + 1))
7886       {
7887         InitMovingField(x, y, MV_DOWN);
7888         started_moving = TRUE;
7889
7890         Tile[x][y] = EL_QUICKSAND_EMPTYING;
7891 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7892         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7893           Store[x][y] = EL_ROCK;
7894 #else
7895         Store[x][y] = EL_ROCK;
7896 #endif
7897
7898         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7899       }
7900       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7901       {
7902         if (!MovDelay[x][y])
7903         {
7904           MovDelay[x][y] = TILEY + 1;
7905
7906           ResetGfxAnimation(x, y);
7907           ResetGfxAnimation(x, y + 1);
7908         }
7909
7910         if (MovDelay[x][y])
7911         {
7912           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7913           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7914
7915           MovDelay[x][y]--;
7916           if (MovDelay[x][y])
7917             return;
7918         }
7919
7920         Tile[x][y] = EL_QUICKSAND_EMPTY;
7921         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7922         Store[x][y + 1] = Store[x][y];
7923         Store[x][y] = 0;
7924
7925         PlayLevelSoundAction(x, y, ACTION_FILLING);
7926       }
7927       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7928       {
7929         if (!MovDelay[x][y])
7930         {
7931           MovDelay[x][y] = TILEY + 1;
7932
7933           ResetGfxAnimation(x, y);
7934           ResetGfxAnimation(x, y + 1);
7935         }
7936
7937         if (MovDelay[x][y])
7938         {
7939           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7940           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7941
7942           MovDelay[x][y]--;
7943           if (MovDelay[x][y])
7944             return;
7945         }
7946
7947         Tile[x][y] = EL_QUICKSAND_EMPTY;
7948         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7949         Store[x][y + 1] = Store[x][y];
7950         Store[x][y] = 0;
7951
7952         PlayLevelSoundAction(x, y, ACTION_FILLING);
7953       }
7954     }
7955     else if (element == EL_QUICKSAND_FAST_FULL)
7956     {
7957       if (IS_FREE(x, y + 1))
7958       {
7959         InitMovingField(x, y, MV_DOWN);
7960         started_moving = TRUE;
7961
7962         Tile[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7963 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7964         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7965           Store[x][y] = EL_ROCK;
7966 #else
7967         Store[x][y] = EL_ROCK;
7968 #endif
7969
7970         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7971       }
7972       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7973       {
7974         if (!MovDelay[x][y])
7975         {
7976           MovDelay[x][y] = TILEY + 1;
7977
7978           ResetGfxAnimation(x, y);
7979           ResetGfxAnimation(x, y + 1);
7980         }
7981
7982         if (MovDelay[x][y])
7983         {
7984           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7985           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7986
7987           MovDelay[x][y]--;
7988           if (MovDelay[x][y])
7989             return;
7990         }
7991
7992         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7993         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7994         Store[x][y + 1] = Store[x][y];
7995         Store[x][y] = 0;
7996
7997         PlayLevelSoundAction(x, y, ACTION_FILLING);
7998       }
7999       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
8000       {
8001         if (!MovDelay[x][y])
8002         {
8003           MovDelay[x][y] = TILEY + 1;
8004
8005           ResetGfxAnimation(x, y);
8006           ResetGfxAnimation(x, y + 1);
8007         }
8008
8009         if (MovDelay[x][y])
8010         {
8011           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
8012           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
8013
8014           MovDelay[x][y]--;
8015           if (MovDelay[x][y])
8016             return;
8017         }
8018
8019         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
8020         Tile[x][y + 1] = EL_QUICKSAND_FULL;
8021         Store[x][y + 1] = Store[x][y];
8022         Store[x][y] = 0;
8023
8024         PlayLevelSoundAction(x, y, ACTION_FILLING);
8025       }
8026     }
8027     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8028              Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
8029     {
8030       InitMovingField(x, y, MV_DOWN);
8031       started_moving = TRUE;
8032
8033       Tile[x][y] = EL_QUICKSAND_FILLING;
8034       Store[x][y] = element;
8035
8036       PlayLevelSoundAction(x, y, ACTION_FILLING);
8037     }
8038     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8039              Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8040     {
8041       InitMovingField(x, y, MV_DOWN);
8042       started_moving = TRUE;
8043
8044       Tile[x][y] = EL_QUICKSAND_FAST_FILLING;
8045       Store[x][y] = element;
8046
8047       PlayLevelSoundAction(x, y, ACTION_FILLING);
8048     }
8049     else if (element == EL_MAGIC_WALL_FULL)
8050     {
8051       if (IS_FREE(x, y + 1))
8052       {
8053         InitMovingField(x, y, MV_DOWN);
8054         started_moving = TRUE;
8055
8056         Tile[x][y] = EL_MAGIC_WALL_EMPTYING;
8057         Store[x][y] = EL_CHANGED(Store[x][y]);
8058       }
8059       else if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
8060       {
8061         if (!MovDelay[x][y])
8062           MovDelay[x][y] = TILEY / 4 + 1;
8063
8064         if (MovDelay[x][y])
8065         {
8066           MovDelay[x][y]--;
8067           if (MovDelay[x][y])
8068             return;
8069         }
8070
8071         Tile[x][y] = EL_MAGIC_WALL_ACTIVE;
8072         Tile[x][y + 1] = EL_MAGIC_WALL_FULL;
8073         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
8074         Store[x][y] = 0;
8075       }
8076     }
8077     else if (element == EL_BD_MAGIC_WALL_FULL)
8078     {
8079       if (IS_FREE(x, y + 1))
8080       {
8081         InitMovingField(x, y, MV_DOWN);
8082         started_moving = TRUE;
8083
8084         Tile[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
8085         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
8086       }
8087       else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
8088       {
8089         if (!MovDelay[x][y])
8090           MovDelay[x][y] = TILEY / 4 + 1;
8091
8092         if (MovDelay[x][y])
8093         {
8094           MovDelay[x][y]--;
8095           if (MovDelay[x][y])
8096             return;
8097         }
8098
8099         Tile[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
8100         Tile[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
8101         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
8102         Store[x][y] = 0;
8103       }
8104     }
8105     else if (element == EL_DC_MAGIC_WALL_FULL)
8106     {
8107       if (IS_FREE(x, y + 1))
8108       {
8109         InitMovingField(x, y, MV_DOWN);
8110         started_moving = TRUE;
8111
8112         Tile[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
8113         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
8114       }
8115       else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
8116       {
8117         if (!MovDelay[x][y])
8118           MovDelay[x][y] = TILEY / 4 + 1;
8119
8120         if (MovDelay[x][y])
8121         {
8122           MovDelay[x][y]--;
8123           if (MovDelay[x][y])
8124             return;
8125         }
8126
8127         Tile[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
8128         Tile[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
8129         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
8130         Store[x][y] = 0;
8131       }
8132     }
8133     else if ((CAN_PASS_MAGIC_WALL(element) &&
8134               (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
8135                Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
8136              (CAN_PASS_DC_MAGIC_WALL(element) &&
8137               (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
8138
8139     {
8140       InitMovingField(x, y, MV_DOWN);
8141       started_moving = TRUE;
8142
8143       Tile[x][y] =
8144         (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
8145          Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
8146          EL_DC_MAGIC_WALL_FILLING);
8147       Store[x][y] = element;
8148     }
8149     else if (CAN_FALL(element) && Tile[x][y + 1] == EL_ACID)
8150     {
8151       SplashAcid(x, y + 1);
8152
8153       InitMovingField(x, y, MV_DOWN);
8154       started_moving = TRUE;
8155
8156       Store[x][y] = EL_ACID;
8157     }
8158     else if (
8159              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8160               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
8161              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
8162               CAN_FALL(element) && WasJustFalling[x][y] &&
8163               (Tile[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
8164
8165              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
8166               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
8167               (Tile[x][y + 1] == EL_BLOCKED)))
8168     {
8169       /* this is needed for a special case not covered by calling "Impact()"
8170          from "ContinueMoving()": if an element moves to a tile directly below
8171          another element which was just falling on that tile (which was empty
8172          in the previous frame), the falling element above would just stop
8173          instead of smashing the element below (in previous version, the above
8174          element was just checked for "moving" instead of "falling", resulting
8175          in incorrect smashes caused by horizontal movement of the above
8176          element; also, the case of the player being the element to smash was
8177          simply not covered here... :-/ ) */
8178
8179       CheckCollision[x][y] = 0;
8180       CheckImpact[x][y] = 0;
8181
8182       Impact(x, y);
8183     }
8184     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
8185     {
8186       if (MovDir[x][y] == MV_NONE)
8187       {
8188         InitMovingField(x, y, MV_DOWN);
8189         started_moving = TRUE;
8190       }
8191     }
8192     else if (IS_FREE(x, y + 1) || Tile[x][y + 1] == EL_DIAMOND_BREAKING)
8193     {
8194       if (WasJustFalling[x][y]) // prevent animation from being restarted
8195         MovDir[x][y] = MV_DOWN;
8196
8197       InitMovingField(x, y, MV_DOWN);
8198       started_moving = TRUE;
8199     }
8200     else if (element == EL_AMOEBA_DROP)
8201     {
8202       Tile[x][y] = EL_AMOEBA_GROWING;
8203       Store[x][y] = EL_AMOEBA_WET;
8204     }
8205     else if (((IS_SLIPPERY(Tile[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8206               (IS_EM_SLIPPERY_WALL(Tile[x][y + 1]) && IS_GEM(element))) &&
8207              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8208              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8209     {
8210       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
8211                                 (IS_FREE(x - 1, y + 1) ||
8212                                  Tile[x - 1][y + 1] == EL_ACID));
8213       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8214                                 (IS_FREE(x + 1, y + 1) ||
8215                                  Tile[x + 1][y + 1] == EL_ACID));
8216       boolean can_fall_any  = (can_fall_left || can_fall_right);
8217       boolean can_fall_both = (can_fall_left && can_fall_right);
8218       int slippery_type = element_info[Tile[x][y + 1]].slippery_type;
8219
8220       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8221       {
8222         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8223           can_fall_right = FALSE;
8224         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8225           can_fall_left = FALSE;
8226         else if (slippery_type == SLIPPERY_ONLY_LEFT)
8227           can_fall_right = FALSE;
8228         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8229           can_fall_left = FALSE;
8230
8231         can_fall_any  = (can_fall_left || can_fall_right);
8232         can_fall_both = FALSE;
8233       }
8234
8235       if (can_fall_both)
8236       {
8237         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8238           can_fall_right = FALSE;       // slip down on left side
8239         else
8240           can_fall_left = !(can_fall_right = RND(2));
8241
8242         can_fall_both = FALSE;
8243       }
8244
8245       if (can_fall_any)
8246       {
8247         // if not determined otherwise, prefer left side for slipping down
8248         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8249         started_moving = TRUE;
8250       }
8251     }
8252     else if (IS_BELT_ACTIVE(Tile[x][y + 1]))
8253     {
8254       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
8255       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8256       int belt_nr = getBeltNrFromBeltActiveElement(Tile[x][y + 1]);
8257       int belt_dir = game.belt_dir[belt_nr];
8258
8259       if ((belt_dir == MV_LEFT  && left_is_free) ||
8260           (belt_dir == MV_RIGHT && right_is_free))
8261       {
8262         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8263
8264         InitMovingField(x, y, belt_dir);
8265         started_moving = TRUE;
8266
8267         Pushed[x][y] = TRUE;
8268         Pushed[nextx][y] = TRUE;
8269
8270         GfxAction[x][y] = ACTION_DEFAULT;
8271       }
8272       else
8273       {
8274         MovDir[x][y] = 0;       // if element was moving, stop it
8275       }
8276     }
8277   }
8278
8279   // not "else if" because of elements that can fall and move (EL_SPRING)
8280   if (CAN_MOVE(element) && !started_moving)
8281   {
8282     int move_pattern = element_info[element].move_pattern;
8283     int newx, newy;
8284
8285     Moving2Blocked(x, y, &newx, &newy);
8286
8287     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8288       return;
8289
8290     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8291         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8292     {
8293       WasJustMoving[x][y] = 0;
8294       CheckCollision[x][y] = 0;
8295
8296       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8297
8298       if (Tile[x][y] != element)        // element has changed
8299         return;
8300     }
8301
8302     if (!MovDelay[x][y])        // start new movement phase
8303     {
8304       // all objects that can change their move direction after each step
8305       // (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall
8306
8307       if (element != EL_YAMYAM &&
8308           element != EL_DARK_YAMYAM &&
8309           element != EL_PACMAN &&
8310           !(move_pattern & MV_ANY_DIRECTION) &&
8311           move_pattern != MV_TURNING_LEFT &&
8312           move_pattern != MV_TURNING_RIGHT &&
8313           move_pattern != MV_TURNING_LEFT_RIGHT &&
8314           move_pattern != MV_TURNING_RIGHT_LEFT &&
8315           move_pattern != MV_TURNING_RANDOM)
8316       {
8317         TurnRound(x, y);
8318
8319         if (MovDelay[x][y] && (element == EL_BUG ||
8320                                element == EL_SPACESHIP ||
8321                                element == EL_SP_SNIKSNAK ||
8322                                element == EL_SP_ELECTRON ||
8323                                element == EL_MOLE))
8324           TEST_DrawLevelField(x, y);
8325       }
8326     }
8327
8328     if (MovDelay[x][y])         // wait some time before next movement
8329     {
8330       MovDelay[x][y]--;
8331
8332       if (element == EL_ROBOT ||
8333           element == EL_YAMYAM ||
8334           element == EL_DARK_YAMYAM)
8335       {
8336         DrawLevelElementAnimationIfNeeded(x, y, element);
8337         PlayLevelSoundAction(x, y, ACTION_WAITING);
8338       }
8339       else if (element == EL_SP_ELECTRON)
8340         DrawLevelElementAnimationIfNeeded(x, y, element);
8341       else if (element == EL_DRAGON)
8342       {
8343         int i;
8344         int dir = MovDir[x][y];
8345         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8346         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8347         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8348                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8349                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8350                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8351         int frame = getGraphicAnimationFrameXY(graphic, x, y);
8352
8353         GfxAction[x][y] = ACTION_ATTACKING;
8354
8355         if (IS_PLAYER(x, y))
8356           DrawPlayerField(x, y);
8357         else
8358           TEST_DrawLevelField(x, y);
8359
8360         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8361
8362         for (i = 1; i <= 3; i++)
8363         {
8364           int xx = x + i * dx;
8365           int yy = y + i * dy;
8366           int sx = SCREENX(xx);
8367           int sy = SCREENY(yy);
8368           int flame_graphic = graphic + (i - 1);
8369
8370           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Tile[xx][yy]))
8371             break;
8372
8373           if (MovDelay[x][y])
8374           {
8375             int flamed = MovingOrBlocked2Element(xx, yy);
8376
8377             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8378               Bang(xx, yy);
8379             else
8380               RemoveMovingField(xx, yy);
8381
8382             ChangeDelay[xx][yy] = 0;
8383
8384             Tile[xx][yy] = EL_FLAMES;
8385
8386             if (IN_SCR_FIELD(sx, sy))
8387             {
8388               TEST_DrawLevelFieldCrumbled(xx, yy);
8389               DrawScreenGraphic(sx, sy, flame_graphic, frame);
8390             }
8391           }
8392           else
8393           {
8394             if (Tile[xx][yy] == EL_FLAMES)
8395               Tile[xx][yy] = EL_EMPTY;
8396             TEST_DrawLevelField(xx, yy);
8397           }
8398         }
8399       }
8400
8401       if (MovDelay[x][y])       // element still has to wait some time
8402       {
8403         PlayLevelSoundAction(x, y, ACTION_WAITING);
8404
8405         return;
8406       }
8407     }
8408
8409     // now make next step
8410
8411     Moving2Blocked(x, y, &newx, &newy); // get next screen position
8412
8413     if (DONT_COLLIDE_WITH(element) &&
8414         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8415         !PLAYER_ENEMY_PROTECTED(newx, newy))
8416     {
8417       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8418
8419       return;
8420     }
8421
8422     else if (CAN_MOVE_INTO_ACID(element) &&
8423              IN_LEV_FIELD(newx, newy) && Tile[newx][newy] == EL_ACID &&
8424              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8425              (MovDir[x][y] == MV_DOWN ||
8426               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8427     {
8428       SplashAcid(newx, newy);
8429       Store[x][y] = EL_ACID;
8430     }
8431     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8432     {
8433       if (Tile[newx][newy] == EL_EXIT_OPEN ||
8434           Tile[newx][newy] == EL_EM_EXIT_OPEN ||
8435           Tile[newx][newy] == EL_STEEL_EXIT_OPEN ||
8436           Tile[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8437       {
8438         RemoveField(x, y);
8439         TEST_DrawLevelField(x, y);
8440
8441         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8442         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8443           DrawGraphicThruMask(SCREENX(newx), SCREENY(newy), el2img(element), 0);
8444
8445         game.friends_still_needed--;
8446         if (!game.friends_still_needed &&
8447             !game.GameOver &&
8448             game.all_players_gone)
8449           LevelSolved();
8450
8451         return;
8452       }
8453       else if (IS_FOOD_PENGUIN(Tile[newx][newy]))
8454       {
8455         if (DigField(local_player, x, y, newx, newy, 0, 0, DF_DIG) == MP_MOVING)
8456           TEST_DrawLevelField(newx, newy);
8457         else
8458           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8459       }
8460       else if (!IS_FREE(newx, newy))
8461       {
8462         GfxAction[x][y] = ACTION_WAITING;
8463
8464         if (IS_PLAYER(x, y))
8465           DrawPlayerField(x, y);
8466         else
8467           TEST_DrawLevelField(x, y);
8468
8469         return;
8470       }
8471     }
8472     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8473     {
8474       if (IS_FOOD_PIG(Tile[newx][newy]))
8475       {
8476         if (IS_MOVING(newx, newy))
8477           RemoveMovingField(newx, newy);
8478         else
8479         {
8480           Tile[newx][newy] = EL_EMPTY;
8481           TEST_DrawLevelField(newx, newy);
8482         }
8483
8484         PlayLevelSound(x, y, SND_PIG_DIGGING);
8485       }
8486       else if (!IS_FREE(newx, newy))
8487       {
8488         if (IS_PLAYER(x, y))
8489           DrawPlayerField(x, y);
8490         else
8491           TEST_DrawLevelField(x, y);
8492
8493         return;
8494       }
8495     }
8496     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8497     {
8498       if (Store[x][y] != EL_EMPTY)
8499       {
8500         boolean can_clone = FALSE;
8501         int xx, yy;
8502
8503         // check if element to clone is still there
8504         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8505         {
8506           if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == Store[x][y])
8507           {
8508             can_clone = TRUE;
8509
8510             break;
8511           }
8512         }
8513
8514         // cannot clone or target field not free anymore -- do not clone
8515         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8516           Store[x][y] = EL_EMPTY;
8517       }
8518
8519       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8520       {
8521         if (IS_MV_DIAGONAL(MovDir[x][y]))
8522         {
8523           int diagonal_move_dir = MovDir[x][y];
8524           int stored = Store[x][y];
8525           int change_delay = 8;
8526           int graphic;
8527
8528           // android is moving diagonally
8529
8530           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8531
8532           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8533           GfxElement[x][y] = EL_EMC_ANDROID;
8534           GfxAction[x][y] = ACTION_SHRINKING;
8535           GfxDir[x][y] = diagonal_move_dir;
8536           ChangeDelay[x][y] = change_delay;
8537
8538           if (Store[x][y] == EL_EMPTY)
8539             Store[x][y] = GfxElementEmpty[x][y];
8540
8541           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8542                                    GfxDir[x][y]);
8543
8544           DrawLevelGraphicAnimation(x, y, graphic);
8545           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8546
8547           if (Tile[newx][newy] == EL_ACID)
8548           {
8549             SplashAcid(newx, newy);
8550
8551             return;
8552           }
8553
8554           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8555
8556           Store[newx][newy] = EL_EMC_ANDROID;
8557           GfxElement[newx][newy] = EL_EMC_ANDROID;
8558           GfxAction[newx][newy] = ACTION_GROWING;
8559           GfxDir[newx][newy] = diagonal_move_dir;
8560           ChangeDelay[newx][newy] = change_delay;
8561
8562           graphic = el_act_dir2img(GfxElement[newx][newy],
8563                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8564
8565           DrawLevelGraphicAnimation(newx, newy, graphic);
8566           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8567
8568           return;
8569         }
8570         else
8571         {
8572           Tile[newx][newy] = EL_EMPTY;
8573           TEST_DrawLevelField(newx, newy);
8574
8575           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8576         }
8577       }
8578       else if (!IS_FREE(newx, newy))
8579       {
8580         return;
8581       }
8582     }
8583     else if (IS_CUSTOM_ELEMENT(element) &&
8584              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8585     {
8586       if (!DigFieldByCE(newx, newy, element))
8587         return;
8588
8589       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8590       {
8591         RunnerVisit[x][y] = FrameCounter;
8592         PlayerVisit[x][y] /= 8;         // expire player visit path
8593       }
8594     }
8595     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8596     {
8597       if (!IS_FREE(newx, newy))
8598       {
8599         if (IS_PLAYER(x, y))
8600           DrawPlayerField(x, y);
8601         else
8602           TEST_DrawLevelField(x, y);
8603
8604         return;
8605       }
8606       else
8607       {
8608         boolean wanna_flame = !RND(10);
8609         int dx = newx - x, dy = newy - y;
8610         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8611         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8612         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8613                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8614         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8615                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8616
8617         if ((wanna_flame ||
8618              IS_CLASSIC_ENEMY(element1) ||
8619              IS_CLASSIC_ENEMY(element2)) &&
8620             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8621             element1 != EL_FLAMES && element2 != EL_FLAMES)
8622         {
8623           ResetGfxAnimation(x, y);
8624           GfxAction[x][y] = ACTION_ATTACKING;
8625
8626           if (IS_PLAYER(x, y))
8627             DrawPlayerField(x, y);
8628           else
8629             TEST_DrawLevelField(x, y);
8630
8631           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8632
8633           MovDelay[x][y] = 50;
8634
8635           Tile[newx][newy] = EL_FLAMES;
8636           if (IN_LEV_FIELD(newx1, newy1) && Tile[newx1][newy1] == EL_EMPTY)
8637             Tile[newx1][newy1] = EL_FLAMES;
8638           if (IN_LEV_FIELD(newx2, newy2) && Tile[newx2][newy2] == EL_EMPTY)
8639             Tile[newx2][newy2] = EL_FLAMES;
8640
8641           return;
8642         }
8643       }
8644     }
8645     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8646              Tile[newx][newy] == EL_DIAMOND)
8647     {
8648       if (IS_MOVING(newx, newy))
8649         RemoveMovingField(newx, newy);
8650       else
8651       {
8652         Tile[newx][newy] = EL_EMPTY;
8653         TEST_DrawLevelField(newx, newy);
8654       }
8655
8656       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8657     }
8658     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8659              IS_FOOD_DARK_YAMYAM(Tile[newx][newy]))
8660     {
8661       if (AmoebaNr[newx][newy])
8662       {
8663         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8664         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8665             Tile[newx][newy] == EL_BD_AMOEBA)
8666           AmoebaCnt[AmoebaNr[newx][newy]]--;
8667       }
8668
8669       if (IS_MOVING(newx, newy))
8670       {
8671         RemoveMovingField(newx, newy);
8672       }
8673       else
8674       {
8675         Tile[newx][newy] = EL_EMPTY;
8676         TEST_DrawLevelField(newx, newy);
8677       }
8678
8679       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8680     }
8681     else if ((element == EL_PACMAN || element == EL_MOLE)
8682              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Tile[newx][newy]))
8683     {
8684       if (AmoebaNr[newx][newy])
8685       {
8686         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8687         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8688             Tile[newx][newy] == EL_BD_AMOEBA)
8689           AmoebaCnt[AmoebaNr[newx][newy]]--;
8690       }
8691
8692       if (element == EL_MOLE)
8693       {
8694         Tile[newx][newy] = EL_AMOEBA_SHRINKING;
8695         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8696
8697         ResetGfxAnimation(x, y);
8698         GfxAction[x][y] = ACTION_DIGGING;
8699         TEST_DrawLevelField(x, y);
8700
8701         MovDelay[newx][newy] = 0;       // start amoeba shrinking delay
8702
8703         return;                         // wait for shrinking amoeba
8704       }
8705       else      // element == EL_PACMAN
8706       {
8707         Tile[newx][newy] = EL_EMPTY;
8708         TEST_DrawLevelField(newx, newy);
8709         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8710       }
8711     }
8712     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8713              (Tile[newx][newy] == EL_AMOEBA_SHRINKING ||
8714               (Tile[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8715     {
8716       // wait for shrinking amoeba to completely disappear
8717       return;
8718     }
8719     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8720     {
8721       // object was running against a wall
8722
8723       TurnRound(x, y);
8724
8725       if (GFX_ELEMENT(element) != EL_SAND)     // !!! FIX THIS (crumble) !!!
8726         DrawLevelElementAnimation(x, y, element);
8727
8728       if (DONT_TOUCH(element))
8729         TestIfBadThingTouchesPlayer(x, y);
8730
8731       return;
8732     }
8733
8734     InitMovingField(x, y, MovDir[x][y]);
8735
8736     PlayLevelSoundAction(x, y, ACTION_MOVING);
8737   }
8738
8739   if (MovDir[x][y])
8740     ContinueMoving(x, y);
8741 }
8742
8743 void ContinueMoving(int x, int y)
8744 {
8745   int element = Tile[x][y];
8746   struct ElementInfo *ei = &element_info[element];
8747   int direction = MovDir[x][y];
8748   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8749   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8750   int newx = x + dx, newy = y + dy;
8751   int stored = Store[x][y];
8752   int stored_new = Store[newx][newy];
8753   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8754   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8755   boolean last_line = (newy == lev_fieldy - 1);
8756   boolean use_step_delay = (GET_MAX_STEP_DELAY(element) != 0);
8757
8758   if (pushed_by_player)         // special case: moving object pushed by player
8759   {
8760     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x, y)->MovPos));
8761   }
8762   else if (use_step_delay)      // special case: moving object has step delay
8763   {
8764     if (!MovDelay[x][y])
8765       MovPos[x][y] += getElementMoveStepsize(x, y);
8766
8767     if (MovDelay[x][y])
8768       MovDelay[x][y]--;
8769     else
8770       MovDelay[x][y] = GET_NEW_STEP_DELAY(element);
8771
8772     if (MovDelay[x][y])
8773     {
8774       TEST_DrawLevelField(x, y);
8775
8776       return;   // element is still waiting
8777     }
8778   }
8779   else                          // normal case: generically moving object
8780   {
8781     MovPos[x][y] += getElementMoveStepsize(x, y);
8782   }
8783
8784   if (ABS(MovPos[x][y]) < TILEX)
8785   {
8786     TEST_DrawLevelField(x, y);
8787
8788     return;     // element is still moving
8789   }
8790
8791   // element reached destination field
8792
8793   Tile[x][y] = EL_EMPTY;
8794   Tile[newx][newy] = element;
8795   MovPos[x][y] = 0;     // force "not moving" for "crumbled sand"
8796
8797   if (Store[x][y] == EL_ACID)   // element is moving into acid pool
8798   {
8799     element = Tile[newx][newy] = EL_ACID;
8800   }
8801   else if (element == EL_MOLE)
8802   {
8803     Tile[x][y] = EL_SAND;
8804
8805     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8806   }
8807   else if (element == EL_QUICKSAND_FILLING)
8808   {
8809     element = Tile[newx][newy] = get_next_element(element);
8810     Store[newx][newy] = Store[x][y];
8811   }
8812   else if (element == EL_QUICKSAND_EMPTYING)
8813   {
8814     Tile[x][y] = get_next_element(element);
8815     element = Tile[newx][newy] = Store[x][y];
8816   }
8817   else if (element == EL_QUICKSAND_FAST_FILLING)
8818   {
8819     element = Tile[newx][newy] = get_next_element(element);
8820     Store[newx][newy] = Store[x][y];
8821   }
8822   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8823   {
8824     Tile[x][y] = get_next_element(element);
8825     element = Tile[newx][newy] = Store[x][y];
8826   }
8827   else if (element == EL_MAGIC_WALL_FILLING)
8828   {
8829     element = Tile[newx][newy] = get_next_element(element);
8830     if (!game.magic_wall_active)
8831       element = Tile[newx][newy] = EL_MAGIC_WALL_DEAD;
8832     Store[newx][newy] = Store[x][y];
8833   }
8834   else if (element == EL_MAGIC_WALL_EMPTYING)
8835   {
8836     Tile[x][y] = get_next_element(element);
8837     if (!game.magic_wall_active)
8838       Tile[x][y] = EL_MAGIC_WALL_DEAD;
8839     element = Tile[newx][newy] = Store[x][y];
8840
8841     InitField(newx, newy, FALSE);
8842   }
8843   else if (element == EL_BD_MAGIC_WALL_FILLING)
8844   {
8845     element = Tile[newx][newy] = get_next_element(element);
8846     if (!game.magic_wall_active)
8847       element = Tile[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8848     Store[newx][newy] = Store[x][y];
8849   }
8850   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8851   {
8852     Tile[x][y] = get_next_element(element);
8853     if (!game.magic_wall_active)
8854       Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
8855     element = Tile[newx][newy] = Store[x][y];
8856
8857     InitField(newx, newy, FALSE);
8858   }
8859   else if (element == EL_DC_MAGIC_WALL_FILLING)
8860   {
8861     element = Tile[newx][newy] = get_next_element(element);
8862     if (!game.magic_wall_active)
8863       element = Tile[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8864     Store[newx][newy] = Store[x][y];
8865   }
8866   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8867   {
8868     Tile[x][y] = get_next_element(element);
8869     if (!game.magic_wall_active)
8870       Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
8871     element = Tile[newx][newy] = Store[x][y];
8872
8873     InitField(newx, newy, FALSE);
8874   }
8875   else if (element == EL_AMOEBA_DROPPING)
8876   {
8877     Tile[x][y] = get_next_element(element);
8878     element = Tile[newx][newy] = Store[x][y];
8879   }
8880   else if (element == EL_SOKOBAN_OBJECT)
8881   {
8882     if (Back[x][y])
8883       Tile[x][y] = Back[x][y];
8884
8885     if (Back[newx][newy])
8886       Tile[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8887
8888     Back[x][y] = Back[newx][newy] = 0;
8889   }
8890
8891   Store[x][y] = EL_EMPTY;
8892   MovPos[x][y] = 0;
8893   MovDir[x][y] = 0;
8894   MovDelay[x][y] = 0;
8895
8896   MovDelay[newx][newy] = 0;
8897
8898   if (CAN_CHANGE_OR_HAS_ACTION(element))
8899   {
8900     // copy element change control values to new field
8901     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8902     ChangePage[newx][newy]  = ChangePage[x][y];
8903     ChangeCount[newx][newy] = ChangeCount[x][y];
8904     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8905   }
8906
8907   CustomValue[newx][newy] = CustomValue[x][y];
8908
8909   ChangeDelay[x][y] = 0;
8910   ChangePage[x][y] = -1;
8911   ChangeCount[x][y] = 0;
8912   ChangeEvent[x][y] = -1;
8913
8914   CustomValue[x][y] = 0;
8915
8916   // copy animation control values to new field
8917   GfxFrame[newx][newy]  = GfxFrame[x][y];
8918   GfxRandom[newx][newy] = GfxRandom[x][y];      // keep same random value
8919   GfxAction[newx][newy] = GfxAction[x][y];      // keep action one frame
8920   GfxDir[newx][newy]    = GfxDir[x][y];         // keep element direction
8921
8922   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8923
8924   // some elements can leave other elements behind after moving
8925   if (ei->move_leave_element != EL_EMPTY &&
8926       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8927       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8928   {
8929     int move_leave_element = ei->move_leave_element;
8930
8931     // this makes it possible to leave the removed element again
8932     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8933       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8934
8935     Tile[x][y] = move_leave_element;
8936
8937     if (element_info[Tile[x][y]].move_direction_initial == MV_START_PREVIOUS)
8938       MovDir[x][y] = direction;
8939
8940     InitField(x, y, FALSE);
8941
8942     if (GFX_CRUMBLED(Tile[x][y]))
8943       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8944
8945     if (IS_PLAYER_ELEMENT(move_leave_element))
8946       RelocatePlayer(x, y, move_leave_element);
8947   }
8948
8949   // do this after checking for left-behind element
8950   ResetGfxAnimation(x, y);      // reset animation values for old field
8951
8952   if (!CAN_MOVE(element) ||
8953       (CAN_FALL(element) && direction == MV_DOWN &&
8954        (element == EL_SPRING ||
8955         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8956         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8957     GfxDir[x][y] = MovDir[newx][newy] = 0;
8958
8959   TEST_DrawLevelField(x, y);
8960   TEST_DrawLevelField(newx, newy);
8961
8962   Stop[newx][newy] = TRUE;      // ignore this element until the next frame
8963
8964   // prevent pushed element from moving on in pushed direction
8965   if (pushed_by_player && CAN_MOVE(element) &&
8966       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8967       !(element_info[element].move_pattern & direction))
8968     TurnRound(newx, newy);
8969
8970   // prevent elements on conveyor belt from moving on in last direction
8971   if (pushed_by_conveyor && CAN_FALL(element) &&
8972       direction & MV_HORIZONTAL)
8973     MovDir[newx][newy] = 0;
8974
8975   if (!pushed_by_player)
8976   {
8977     int nextx = newx + dx, nexty = newy + dy;
8978     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8979
8980     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8981
8982     if (CAN_FALL(element) && direction == MV_DOWN)
8983       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8984
8985     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8986       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8987
8988     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8989       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8990   }
8991
8992   if (DONT_TOUCH(element))      // object may be nasty to player or others
8993   {
8994     TestIfBadThingTouchesPlayer(newx, newy);
8995     TestIfBadThingTouchesFriend(newx, newy);
8996
8997     if (!IS_CUSTOM_ELEMENT(element))
8998       TestIfBadThingTouchesOtherBadThing(newx, newy);
8999   }
9000   else if (element == EL_PENGUIN)
9001     TestIfFriendTouchesBadThing(newx, newy);
9002
9003   if (DONT_GET_HIT_BY(element))
9004   {
9005     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
9006   }
9007
9008   // give the player one last chance (one more frame) to move away
9009   if (CAN_FALL(element) && direction == MV_DOWN &&
9010       (last_line || (!IS_FREE(x, newy + 1) &&
9011                      (!IS_PLAYER(x, newy + 1) ||
9012                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
9013     Impact(x, newy);
9014
9015   if (pushed_by_player && !game.use_change_when_pushing_bug)
9016   {
9017     int push_side = MV_DIR_OPPOSITE(direction);
9018     struct PlayerInfo *player = PLAYERINFO(x, y);
9019
9020     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
9021                                player->index_bit, push_side);
9022     CheckTriggeredElementChangeByPlayer(newx, newy, element, CE_PLAYER_PUSHES_X,
9023                                         player->index_bit, push_side);
9024   }
9025
9026   if (element == EL_EMC_ANDROID && pushed_by_player)    // make another move
9027     MovDelay[newx][newy] = 1;
9028
9029   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
9030
9031   TestIfElementTouchesCustomElement(x, y);      // empty or new element
9032   TestIfElementHitsCustomElement(newx, newy, direction);
9033   TestIfPlayerTouchesCustomElement(newx, newy);
9034   TestIfElementTouchesCustomElement(newx, newy);
9035
9036   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
9037       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
9038     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
9039                              MV_DIR_OPPOSITE(direction));
9040 }
9041
9042 int AmoebaNeighbourNr(int ax, int ay)
9043 {
9044   int i;
9045   int element = Tile[ax][ay];
9046   int group_nr = 0;
9047   struct XY *xy = xy_topdown;
9048
9049   for (i = 0; i < NUM_DIRECTIONS; i++)
9050   {
9051     int x = ax + xy[i].x;
9052     int y = ay + xy[i].y;
9053
9054     if (!IN_LEV_FIELD(x, y))
9055       continue;
9056
9057     if (Tile[x][y] == element && AmoebaNr[x][y] > 0)
9058       group_nr = AmoebaNr[x][y];
9059   }
9060
9061   return group_nr;
9062 }
9063
9064 static void AmoebaMerge(int ax, int ay)
9065 {
9066   int i, x, y, xx, yy;
9067   int new_group_nr = AmoebaNr[ax][ay];
9068   struct XY *xy = xy_topdown;
9069
9070   if (new_group_nr == 0)
9071     return;
9072
9073   for (i = 0; i < NUM_DIRECTIONS; i++)
9074   {
9075     x = ax + xy[i].x;
9076     y = ay + xy[i].y;
9077
9078     if (!IN_LEV_FIELD(x, y))
9079       continue;
9080
9081     if ((Tile[x][y] == EL_AMOEBA_FULL ||
9082          Tile[x][y] == EL_BD_AMOEBA ||
9083          Tile[x][y] == EL_AMOEBA_DEAD) &&
9084         AmoebaNr[x][y] != new_group_nr)
9085     {
9086       int old_group_nr = AmoebaNr[x][y];
9087
9088       if (old_group_nr == 0)
9089         return;
9090
9091       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
9092       AmoebaCnt[old_group_nr] = 0;
9093       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
9094       AmoebaCnt2[old_group_nr] = 0;
9095
9096       SCAN_PLAYFIELD(xx, yy)
9097       {
9098         if (AmoebaNr[xx][yy] == old_group_nr)
9099           AmoebaNr[xx][yy] = new_group_nr;
9100       }
9101     }
9102   }
9103 }
9104
9105 void AmoebaToDiamond(int ax, int ay)
9106 {
9107   int i, x, y;
9108
9109   if (Tile[ax][ay] == EL_AMOEBA_DEAD)
9110   {
9111     int group_nr = AmoebaNr[ax][ay];
9112
9113 #ifdef DEBUG
9114     if (group_nr == 0)
9115     {
9116       Debug("game:playing:AmoebaToDiamond", "ax = %d, ay = %d", ax, ay);
9117       Debug("game:playing:AmoebaToDiamond", "This should never happen!");
9118
9119       return;
9120     }
9121 #endif
9122
9123     SCAN_PLAYFIELD(x, y)
9124     {
9125       if (Tile[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9126       {
9127         AmoebaNr[x][y] = 0;
9128         Tile[x][y] = EL_AMOEBA_TO_DIAMOND;
9129       }
9130     }
9131
9132     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9133                             SND_AMOEBA_TURNING_TO_GEM :
9134                             SND_AMOEBA_TURNING_TO_ROCK));
9135     Bang(ax, ay);
9136   }
9137   else
9138   {
9139     struct XY *xy = xy_topdown;
9140
9141     for (i = 0; i < NUM_DIRECTIONS; i++)
9142     {
9143       x = ax + xy[i].x;
9144       y = ay + xy[i].y;
9145
9146       if (!IN_LEV_FIELD(x, y))
9147         continue;
9148
9149       if (Tile[x][y] == EL_AMOEBA_TO_DIAMOND)
9150       {
9151         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9152                               SND_AMOEBA_TURNING_TO_GEM :
9153                               SND_AMOEBA_TURNING_TO_ROCK));
9154         Bang(x, y);
9155       }
9156     }
9157   }
9158 }
9159
9160 static void AmoebaToDiamondBD(int ax, int ay, int new_element)
9161 {
9162   int x, y;
9163   int group_nr = AmoebaNr[ax][ay];
9164   boolean done = FALSE;
9165
9166 #ifdef DEBUG
9167   if (group_nr == 0)
9168   {
9169     Debug("game:playing:AmoebaToDiamondBD", "ax = %d, ay = %d", ax, ay);
9170     Debug("game:playing:AmoebaToDiamondBD", "This should never happen!");
9171
9172     return;
9173   }
9174 #endif
9175
9176   SCAN_PLAYFIELD(x, y)
9177   {
9178     if (AmoebaNr[x][y] == group_nr &&
9179         (Tile[x][y] == EL_AMOEBA_DEAD ||
9180          Tile[x][y] == EL_BD_AMOEBA ||
9181          Tile[x][y] == EL_AMOEBA_GROWING))
9182     {
9183       AmoebaNr[x][y] = 0;
9184       Tile[x][y] = new_element;
9185       InitField(x, y, FALSE);
9186       TEST_DrawLevelField(x, y);
9187       done = TRUE;
9188     }
9189   }
9190
9191   if (done)
9192     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9193                             SND_BD_AMOEBA_TURNING_TO_ROCK :
9194                             SND_BD_AMOEBA_TURNING_TO_GEM));
9195 }
9196
9197 static void AmoebaGrowing(int x, int y)
9198 {
9199   static DelayCounter sound_delay = { 0 };
9200
9201   if (!MovDelay[x][y])          // start new growing cycle
9202   {
9203     MovDelay[x][y] = 7;
9204
9205     if (DelayReached(&sound_delay))
9206     {
9207       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9208       sound_delay.value = 30;
9209     }
9210   }
9211
9212   if (MovDelay[x][y])           // wait some time before growing bigger
9213   {
9214     MovDelay[x][y]--;
9215     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9216     {
9217       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9218                                            6 - MovDelay[x][y]);
9219
9220       DrawLevelGraphic(x, y, IMG_AMOEBA_GROWING, frame);
9221     }
9222
9223     if (!MovDelay[x][y])
9224     {
9225       Tile[x][y] = Store[x][y];
9226       Store[x][y] = 0;
9227       TEST_DrawLevelField(x, y);
9228     }
9229   }
9230 }
9231
9232 static void AmoebaShrinking(int x, int y)
9233 {
9234   static DelayCounter sound_delay = { 0 };
9235
9236   if (!MovDelay[x][y])          // start new shrinking cycle
9237   {
9238     MovDelay[x][y] = 7;
9239
9240     if (DelayReached(&sound_delay))
9241       sound_delay.value = 30;
9242   }
9243
9244   if (MovDelay[x][y])           // wait some time before shrinking
9245   {
9246     MovDelay[x][y]--;
9247     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9248     {
9249       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9250                                            6 - MovDelay[x][y]);
9251
9252       DrawLevelGraphic(x, y, IMG_AMOEBA_SHRINKING, frame);
9253     }
9254
9255     if (!MovDelay[x][y])
9256     {
9257       Tile[x][y] = EL_EMPTY;
9258       TEST_DrawLevelField(x, y);
9259
9260       // don't let mole enter this field in this cycle;
9261       // (give priority to objects falling to this field from above)
9262       Stop[x][y] = TRUE;
9263     }
9264   }
9265 }
9266
9267 static void AmoebaReproduce(int ax, int ay)
9268 {
9269   int i;
9270   int element = Tile[ax][ay];
9271   int graphic = el2img(element);
9272   int newax = ax, neway = ay;
9273   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9274   struct XY *xy = xy_topdown;
9275
9276   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9277   {
9278     Tile[ax][ay] = EL_AMOEBA_DEAD;
9279     TEST_DrawLevelField(ax, ay);
9280     return;
9281   }
9282
9283   if (IS_ANIMATED(graphic))
9284     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9285
9286   if (!MovDelay[ax][ay])        // start making new amoeba field
9287     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9288
9289   if (MovDelay[ax][ay])         // wait some time before making new amoeba
9290   {
9291     MovDelay[ax][ay]--;
9292     if (MovDelay[ax][ay])
9293       return;
9294   }
9295
9296   if (can_drop)                 // EL_AMOEBA_WET or EL_EMC_DRIPPER
9297   {
9298     int start = RND(4);
9299     int x = ax + xy[start].x;
9300     int y = ay + xy[start].y;
9301
9302     if (!IN_LEV_FIELD(x, y))
9303       return;
9304
9305     if (IS_FREE(x, y) ||
9306         CAN_GROW_INTO(Tile[x][y]) ||
9307         Tile[x][y] == EL_QUICKSAND_EMPTY ||
9308         Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9309     {
9310       newax = x;
9311       neway = y;
9312     }
9313
9314     if (newax == ax && neway == ay)
9315       return;
9316   }
9317   else                          // normal or "filled" (BD style) amoeba
9318   {
9319     int start = RND(4);
9320     boolean waiting_for_player = FALSE;
9321
9322     for (i = 0; i < NUM_DIRECTIONS; i++)
9323     {
9324       int j = (start + i) % 4;
9325       int x = ax + xy[j].x;
9326       int y = ay + xy[j].y;
9327
9328       if (!IN_LEV_FIELD(x, y))
9329         continue;
9330
9331       if (IS_FREE(x, y) ||
9332           CAN_GROW_INTO(Tile[x][y]) ||
9333           Tile[x][y] == EL_QUICKSAND_EMPTY ||
9334           Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9335       {
9336         newax = x;
9337         neway = y;
9338         break;
9339       }
9340       else if (IS_PLAYER(x, y))
9341         waiting_for_player = TRUE;
9342     }
9343
9344     if (newax == ax && neway == ay)             // amoeba cannot grow
9345     {
9346       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9347       {
9348         Tile[ax][ay] = EL_AMOEBA_DEAD;
9349         TEST_DrawLevelField(ax, ay);
9350         AmoebaCnt[AmoebaNr[ax][ay]]--;
9351
9352         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   // amoeba is completely dead
9353         {
9354           if (element == EL_AMOEBA_FULL)
9355             AmoebaToDiamond(ax, ay);
9356           else if (element == EL_BD_AMOEBA)
9357             AmoebaToDiamondBD(ax, ay, level.amoeba_content);
9358         }
9359       }
9360       return;
9361     }
9362     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9363     {
9364       // amoeba gets larger by growing in some direction
9365
9366       int new_group_nr = AmoebaNr[ax][ay];
9367
9368 #ifdef DEBUG
9369   if (new_group_nr == 0)
9370   {
9371     Debug("game:playing:AmoebaReproduce", "newax = %d, neway = %d",
9372           newax, neway);
9373     Debug("game:playing:AmoebaReproduce", "This should never happen!");
9374
9375     return;
9376   }
9377 #endif
9378
9379       AmoebaNr[newax][neway] = new_group_nr;
9380       AmoebaCnt[new_group_nr]++;
9381       AmoebaCnt2[new_group_nr]++;
9382
9383       // if amoeba touches other amoeba(s) after growing, unify them
9384       AmoebaMerge(newax, neway);
9385
9386       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9387       {
9388         AmoebaToDiamondBD(newax, neway, EL_BD_ROCK);
9389         return;
9390       }
9391     }
9392   }
9393
9394   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9395       (neway == lev_fieldy - 1 && newax != ax))
9396   {
9397     Tile[newax][neway] = EL_AMOEBA_GROWING;     // creation of new amoeba
9398     Store[newax][neway] = element;
9399   }
9400   else if (neway == ay || element == EL_EMC_DRIPPER)
9401   {
9402     Tile[newax][neway] = EL_AMOEBA_DROP;        // drop left/right of amoeba
9403
9404     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9405   }
9406   else
9407   {
9408     InitMovingField(ax, ay, MV_DOWN);           // drop dripping from amoeba
9409     Tile[ax][ay] = EL_AMOEBA_DROPPING;
9410     Store[ax][ay] = EL_AMOEBA_DROP;
9411     ContinueMoving(ax, ay);
9412     return;
9413   }
9414
9415   TEST_DrawLevelField(newax, neway);
9416 }
9417
9418 static void Life(int ax, int ay)
9419 {
9420   int x1, y1, x2, y2;
9421   int life_time = 40;
9422   int element = Tile[ax][ay];
9423   int graphic = el2img(element);
9424   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9425                          level.biomaze);
9426   boolean changed = FALSE;
9427
9428   if (IS_ANIMATED(graphic))
9429     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9430
9431   if (Stop[ax][ay])
9432     return;
9433
9434   if (!MovDelay[ax][ay])        // start new "game of life" cycle
9435     MovDelay[ax][ay] = life_time;
9436
9437   if (MovDelay[ax][ay])         // wait some time before next cycle
9438   {
9439     MovDelay[ax][ay]--;
9440     if (MovDelay[ax][ay])
9441       return;
9442   }
9443
9444   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9445   {
9446     int xx = ax+x1, yy = ay+y1;
9447     int old_element = Tile[xx][yy];
9448     int num_neighbours = 0;
9449
9450     if (!IN_LEV_FIELD(xx, yy))
9451       continue;
9452
9453     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9454     {
9455       int x = xx+x2, y = yy+y2;
9456
9457       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9458         continue;
9459
9460       boolean is_player_cell = (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y));
9461       boolean is_neighbour = FALSE;
9462
9463       if (level.use_life_bugs)
9464         is_neighbour =
9465           (((Tile[x][y] == element || is_player_cell) && !Stop[x][y]) ||
9466            (IS_FREE(x, y)                             &&  Stop[x][y]));
9467       else
9468         is_neighbour =
9469           (Last[x][y] == element || is_player_cell);
9470
9471       if (is_neighbour)
9472         num_neighbours++;
9473     }
9474
9475     boolean is_free = FALSE;
9476
9477     if (level.use_life_bugs)
9478       is_free = (IS_FREE(xx, yy));
9479     else
9480       is_free = (IS_FREE(xx, yy) && Last[xx][yy] == EL_EMPTY);
9481
9482     if (xx == ax && yy == ay)           // field in the middle
9483     {
9484       if (num_neighbours < life_parameter[0] ||
9485           num_neighbours > life_parameter[1])
9486       {
9487         Tile[xx][yy] = EL_EMPTY;
9488         if (Tile[xx][yy] != old_element)
9489           TEST_DrawLevelField(xx, yy);
9490         Stop[xx][yy] = TRUE;
9491         changed = TRUE;
9492       }
9493     }
9494     else if (is_free || CAN_GROW_INTO(Tile[xx][yy]))
9495     {                                   // free border field
9496       if (num_neighbours >= life_parameter[2] &&
9497           num_neighbours <= life_parameter[3])
9498       {
9499         Tile[xx][yy] = element;
9500         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time - 1);
9501         if (Tile[xx][yy] != old_element)
9502           TEST_DrawLevelField(xx, yy);
9503         Stop[xx][yy] = TRUE;
9504         changed = TRUE;
9505       }
9506     }
9507   }
9508
9509   if (changed)
9510     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9511                    SND_GAME_OF_LIFE_GROWING);
9512 }
9513
9514 static void InitRobotWheel(int x, int y)
9515 {
9516   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9517 }
9518
9519 static void RunRobotWheel(int x, int y)
9520 {
9521   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9522 }
9523
9524 static void StopRobotWheel(int x, int y)
9525 {
9526   if (game.robot_wheel_x == x &&
9527       game.robot_wheel_y == y)
9528   {
9529     game.robot_wheel_x = -1;
9530     game.robot_wheel_y = -1;
9531     game.robot_wheel_active = FALSE;
9532   }
9533 }
9534
9535 static void InitTimegateWheel(int x, int y)
9536 {
9537   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9538 }
9539
9540 static void RunTimegateWheel(int x, int y)
9541 {
9542   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9543 }
9544
9545 static void InitMagicBallDelay(int x, int y)
9546 {
9547   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9548 }
9549
9550 static void ActivateMagicBall(int bx, int by)
9551 {
9552   int x, y;
9553
9554   if (level.ball_random)
9555   {
9556     int pos_border = RND(8);    // select one of the eight border elements
9557     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9558     int xx = pos_content % 3;
9559     int yy = pos_content / 3;
9560
9561     x = bx - 1 + xx;
9562     y = by - 1 + yy;
9563
9564     if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9565       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9566   }
9567   else
9568   {
9569     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9570     {
9571       int xx = x - bx + 1;
9572       int yy = y - by + 1;
9573
9574       if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9575         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9576     }
9577   }
9578
9579   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9580 }
9581
9582 static void CheckExit(int x, int y)
9583 {
9584   if (game.gems_still_needed > 0 ||
9585       game.sokoban_fields_still_needed > 0 ||
9586       game.sokoban_objects_still_needed > 0 ||
9587       game.lights_still_needed > 0)
9588   {
9589     int element = Tile[x][y];
9590     int graphic = el2img(element);
9591
9592     if (IS_ANIMATED(graphic))
9593       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9594
9595     return;
9596   }
9597
9598   // do not re-open exit door closed after last player
9599   if (game.all_players_gone)
9600     return;
9601
9602   Tile[x][y] = EL_EXIT_OPENING;
9603
9604   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9605 }
9606
9607 static void CheckExitEM(int x, int y)
9608 {
9609   if (game.gems_still_needed > 0 ||
9610       game.sokoban_fields_still_needed > 0 ||
9611       game.sokoban_objects_still_needed > 0 ||
9612       game.lights_still_needed > 0)
9613   {
9614     int element = Tile[x][y];
9615     int graphic = el2img(element);
9616
9617     if (IS_ANIMATED(graphic))
9618       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9619
9620     return;
9621   }
9622
9623   // do not re-open exit door closed after last player
9624   if (game.all_players_gone)
9625     return;
9626
9627   Tile[x][y] = EL_EM_EXIT_OPENING;
9628
9629   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9630 }
9631
9632 static void CheckExitSteel(int x, int y)
9633 {
9634   if (game.gems_still_needed > 0 ||
9635       game.sokoban_fields_still_needed > 0 ||
9636       game.sokoban_objects_still_needed > 0 ||
9637       game.lights_still_needed > 0)
9638   {
9639     int element = Tile[x][y];
9640     int graphic = el2img(element);
9641
9642     if (IS_ANIMATED(graphic))
9643       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9644
9645     return;
9646   }
9647
9648   // do not re-open exit door closed after last player
9649   if (game.all_players_gone)
9650     return;
9651
9652   Tile[x][y] = EL_STEEL_EXIT_OPENING;
9653
9654   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9655 }
9656
9657 static void CheckExitSteelEM(int x, int y)
9658 {
9659   if (game.gems_still_needed > 0 ||
9660       game.sokoban_fields_still_needed > 0 ||
9661       game.sokoban_objects_still_needed > 0 ||
9662       game.lights_still_needed > 0)
9663   {
9664     int element = Tile[x][y];
9665     int graphic = el2img(element);
9666
9667     if (IS_ANIMATED(graphic))
9668       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9669
9670     return;
9671   }
9672
9673   // do not re-open exit door closed after last player
9674   if (game.all_players_gone)
9675     return;
9676
9677   Tile[x][y] = EL_EM_STEEL_EXIT_OPENING;
9678
9679   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9680 }
9681
9682 static void CheckExitSP(int x, int y)
9683 {
9684   if (game.gems_still_needed > 0)
9685   {
9686     int element = Tile[x][y];
9687     int graphic = el2img(element);
9688
9689     if (IS_ANIMATED(graphic))
9690       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9691
9692     return;
9693   }
9694
9695   // do not re-open exit door closed after last player
9696   if (game.all_players_gone)
9697     return;
9698
9699   Tile[x][y] = EL_SP_EXIT_OPENING;
9700
9701   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9702 }
9703
9704 static void CloseAllOpenTimegates(void)
9705 {
9706   int x, y;
9707
9708   SCAN_PLAYFIELD(x, y)
9709   {
9710     int element = Tile[x][y];
9711
9712     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9713     {
9714       Tile[x][y] = EL_TIMEGATE_CLOSING;
9715
9716       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9717     }
9718   }
9719 }
9720
9721 static void DrawTwinkleOnField(int x, int y)
9722 {
9723   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9724     return;
9725
9726   if (Tile[x][y] == EL_BD_DIAMOND)
9727     return;
9728
9729   if (MovDelay[x][y] == 0)      // next animation frame
9730     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9731
9732   if (MovDelay[x][y] != 0)      // wait some time before next frame
9733   {
9734     MovDelay[x][y]--;
9735
9736     DrawLevelElementAnimation(x, y, Tile[x][y]);
9737
9738     if (MovDelay[x][y] != 0)
9739     {
9740       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9741                                            10 - MovDelay[x][y]);
9742
9743       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9744     }
9745   }
9746 }
9747
9748 static void WallGrowing(int x, int y)
9749 {
9750   int delay = 6;
9751
9752   if (!MovDelay[x][y])          // next animation frame
9753     MovDelay[x][y] = 3 * delay;
9754
9755   if (MovDelay[x][y])           // wait some time before next frame
9756   {
9757     MovDelay[x][y]--;
9758
9759     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9760     {
9761       int graphic = el_dir2img(Tile[x][y], GfxDir[x][y]);
9762       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9763
9764       DrawLevelGraphic(x, y, graphic, frame);
9765     }
9766
9767     if (!MovDelay[x][y])
9768     {
9769       if (MovDir[x][y] == MV_LEFT)
9770       {
9771         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Tile[x - 1][y]))
9772           TEST_DrawLevelField(x - 1, y);
9773       }
9774       else if (MovDir[x][y] == MV_RIGHT)
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_UP)
9780       {
9781         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Tile[x][y - 1]))
9782           TEST_DrawLevelField(x, y - 1);
9783       }
9784       else
9785       {
9786         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Tile[x][y + 1]))
9787           TEST_DrawLevelField(x, y + 1);
9788       }
9789
9790       Tile[x][y] = Store[x][y];
9791       Store[x][y] = 0;
9792       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9793       TEST_DrawLevelField(x, y);
9794     }
9795   }
9796 }
9797
9798 static void CheckWallGrowing(int ax, int ay)
9799 {
9800   int element = Tile[ax][ay];
9801   int graphic = el2img(element);
9802   boolean free_top    = FALSE;
9803   boolean free_bottom = FALSE;
9804   boolean free_left   = FALSE;
9805   boolean free_right  = FALSE;
9806   boolean stop_top    = FALSE;
9807   boolean stop_bottom = FALSE;
9808   boolean stop_left   = FALSE;
9809   boolean stop_right  = FALSE;
9810   boolean new_wall    = FALSE;
9811
9812   boolean is_steelwall  = (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9813                            element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9814                            element == EL_EXPANDABLE_STEELWALL_ANY);
9815
9816   boolean grow_vertical   = (element == EL_EXPANDABLE_WALL_VERTICAL ||
9817                              element == EL_EXPANDABLE_WALL_ANY ||
9818                              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9819                              element == EL_EXPANDABLE_STEELWALL_ANY);
9820
9821   boolean grow_horizontal = (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9822                              element == EL_EXPANDABLE_WALL_ANY ||
9823                              element == EL_EXPANDABLE_WALL ||
9824                              element == EL_BD_EXPANDABLE_WALL ||
9825                              element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9826                              element == EL_EXPANDABLE_STEELWALL_ANY);
9827
9828   boolean stop_vertical   = (element == EL_EXPANDABLE_WALL_VERTICAL ||
9829                              element == EL_EXPANDABLE_STEELWALL_VERTICAL);
9830
9831   boolean stop_horizontal = (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9832                              element == EL_EXPANDABLE_WALL ||
9833                              element == EL_EXPANDABLE_STEELWALL_HORIZONTAL);
9834
9835   int wall_growing = (is_steelwall ?
9836                       EL_EXPANDABLE_STEELWALL_GROWING :
9837                       EL_EXPANDABLE_WALL_GROWING);
9838
9839   int gfx_wall_growing_up    = (is_steelwall ?
9840                                 IMG_EXPANDABLE_STEELWALL_GROWING_UP :
9841                                 IMG_EXPANDABLE_WALL_GROWING_UP);
9842   int gfx_wall_growing_down  = (is_steelwall ?
9843                                 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN :
9844                                 IMG_EXPANDABLE_WALL_GROWING_DOWN);
9845   int gfx_wall_growing_left  = (is_steelwall ?
9846                                 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT :
9847                                 IMG_EXPANDABLE_WALL_GROWING_LEFT);
9848   int gfx_wall_growing_right = (is_steelwall ?
9849                                 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT :
9850                                 IMG_EXPANDABLE_WALL_GROWING_RIGHT);
9851
9852   if (IS_ANIMATED(graphic))
9853     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9854
9855   if (!MovDelay[ax][ay])        // start building new wall
9856     MovDelay[ax][ay] = 6;
9857
9858   if (MovDelay[ax][ay])         // wait some time before building new wall
9859   {
9860     MovDelay[ax][ay]--;
9861     if (MovDelay[ax][ay])
9862       return;
9863   }
9864
9865   if (IN_LEV_FIELD(ax, ay - 1) && IS_FREE(ax, ay - 1))
9866     free_top = TRUE;
9867   if (IN_LEV_FIELD(ax, ay + 1) && IS_FREE(ax, ay + 1))
9868     free_bottom = TRUE;
9869   if (IN_LEV_FIELD(ax - 1, ay) && IS_FREE(ax - 1, ay))
9870     free_left = TRUE;
9871   if (IN_LEV_FIELD(ax + 1, ay) && IS_FREE(ax + 1, ay))
9872     free_right = TRUE;
9873
9874   if (grow_vertical)
9875   {
9876     if (free_top)
9877     {
9878       Tile[ax][ay - 1] = wall_growing;
9879       Store[ax][ay - 1] = element;
9880       GfxDir[ax][ay - 1] = MovDir[ax][ay - 1] = MV_UP;
9881
9882       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay - 1)))
9883         DrawLevelGraphic(ax, ay - 1, gfx_wall_growing_up, 0);
9884
9885       new_wall = TRUE;
9886     }
9887
9888     if (free_bottom)
9889     {
9890       Tile[ax][ay + 1] = wall_growing;
9891       Store[ax][ay + 1] = element;
9892       GfxDir[ax][ay + 1] = MovDir[ax][ay + 1] = MV_DOWN;
9893
9894       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay + 1)))
9895         DrawLevelGraphic(ax, ay + 1, gfx_wall_growing_down, 0);
9896
9897       new_wall = TRUE;
9898     }
9899   }
9900
9901   if (grow_horizontal)
9902   {
9903     if (free_left)
9904     {
9905       Tile[ax - 1][ay] = wall_growing;
9906       Store[ax - 1][ay] = element;
9907       GfxDir[ax - 1][ay] = MovDir[ax - 1][ay] = MV_LEFT;
9908
9909       if (IN_SCR_FIELD(SCREENX(ax - 1), SCREENY(ay)))
9910         DrawLevelGraphic(ax - 1, ay, gfx_wall_growing_left, 0);
9911
9912       new_wall = TRUE;
9913     }
9914
9915     if (free_right)
9916     {
9917       Tile[ax + 1][ay] = wall_growing;
9918       Store[ax + 1][ay] = element;
9919       GfxDir[ax + 1][ay] = MovDir[ax + 1][ay] = MV_RIGHT;
9920
9921       if (IN_SCR_FIELD(SCREENX(ax + 1), SCREENY(ay)))
9922         DrawLevelGraphic(ax + 1, ay, gfx_wall_growing_right, 0);
9923
9924       new_wall = TRUE;
9925     }
9926   }
9927
9928   if (element == EL_EXPANDABLE_WALL && (free_left || free_right))
9929     TEST_DrawLevelField(ax, ay);
9930
9931   if (!IN_LEV_FIELD(ax, ay - 1) || IS_WALL(Tile[ax][ay - 1]))
9932     stop_top = TRUE;
9933   if (!IN_LEV_FIELD(ax, ay + 1) || IS_WALL(Tile[ax][ay + 1]))
9934     stop_bottom = TRUE;
9935   if (!IN_LEV_FIELD(ax - 1, ay) || IS_WALL(Tile[ax - 1][ay]))
9936     stop_left = TRUE;
9937   if (!IN_LEV_FIELD(ax + 1, ay) || IS_WALL(Tile[ax + 1][ay]))
9938     stop_right = TRUE;
9939
9940   if (((stop_top && stop_bottom) || stop_horizontal) &&
9941       ((stop_left && stop_right) || stop_vertical))
9942     Tile[ax][ay] = (is_steelwall ? EL_STEELWALL : EL_WALL);
9943
9944   if (new_wall)
9945     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9946 }
9947
9948 static void CheckForDragon(int x, int y)
9949 {
9950   int i, j;
9951   boolean dragon_found = FALSE;
9952   struct XY *xy = xy_topdown;
9953
9954   for (i = 0; i < NUM_DIRECTIONS; i++)
9955   {
9956     for (j = 0; j < 4; j++)
9957     {
9958       int xx = x + j * xy[i].x;
9959       int yy = y + j * xy[i].y;
9960
9961       if (IN_LEV_FIELD(xx, yy) &&
9962           (Tile[xx][yy] == EL_FLAMES || Tile[xx][yy] == EL_DRAGON))
9963       {
9964         if (Tile[xx][yy] == EL_DRAGON)
9965           dragon_found = TRUE;
9966       }
9967       else
9968         break;
9969     }
9970   }
9971
9972   if (!dragon_found)
9973   {
9974     for (i = 0; i < NUM_DIRECTIONS; i++)
9975     {
9976       for (j = 0; j < 3; j++)
9977       {
9978         int xx = x + j * xy[i].x;
9979         int yy = y + j * xy[i].y;
9980
9981         if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == EL_FLAMES)
9982         {
9983           Tile[xx][yy] = EL_EMPTY;
9984           TEST_DrawLevelField(xx, yy);
9985         }
9986         else
9987           break;
9988       }
9989     }
9990   }
9991 }
9992
9993 static void InitBuggyBase(int x, int y)
9994 {
9995   int element = Tile[x][y];
9996   int activating_delay = FRAMES_PER_SECOND / 4;
9997
9998   ChangeDelay[x][y] =
9999     (element == EL_SP_BUGGY_BASE ?
10000      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
10001      element == EL_SP_BUGGY_BASE_ACTIVATING ?
10002      activating_delay :
10003      element == EL_SP_BUGGY_BASE_ACTIVE ?
10004      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
10005 }
10006
10007 static void WarnBuggyBase(int x, int y)
10008 {
10009   int i;
10010   struct XY *xy = xy_topdown;
10011
10012   for (i = 0; i < NUM_DIRECTIONS; i++)
10013   {
10014     int xx = x + xy[i].x;
10015     int yy = y + xy[i].y;
10016
10017     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
10018     {
10019       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
10020
10021       break;
10022     }
10023   }
10024 }
10025
10026 static void InitTrap(int x, int y)
10027 {
10028   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
10029 }
10030
10031 static void ActivateTrap(int x, int y)
10032 {
10033   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
10034 }
10035
10036 static void ChangeActiveTrap(int x, int y)
10037 {
10038   int graphic = IMG_TRAP_ACTIVE;
10039
10040   // if new animation frame was drawn, correct crumbled sand border
10041   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
10042     TEST_DrawLevelFieldCrumbled(x, y);
10043 }
10044
10045 static int getSpecialActionElement(int element, int number, int base_element)
10046 {
10047   return (element != EL_EMPTY ? element :
10048           number != -1 ? base_element + number - 1 :
10049           EL_EMPTY);
10050 }
10051
10052 static int getModifiedActionNumber(int value_old, int operator, int operand,
10053                                    int value_min, int value_max)
10054 {
10055   int value_new = (operator == CA_MODE_SET      ? operand :
10056                    operator == CA_MODE_ADD      ? value_old + operand :
10057                    operator == CA_MODE_SUBTRACT ? value_old - operand :
10058                    operator == CA_MODE_MULTIPLY ? value_old * operand :
10059                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
10060                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
10061                    value_old);
10062
10063   return (value_new < value_min ? value_min :
10064           value_new > value_max ? value_max :
10065           value_new);
10066 }
10067
10068 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10069 {
10070   struct ElementInfo *ei = &element_info[element];
10071   struct ElementChangeInfo *change = &ei->change_page[page];
10072   int target_element = change->target_element;
10073   int action_type = change->action_type;
10074   int action_mode = change->action_mode;
10075   int action_arg = change->action_arg;
10076   int action_element = change->action_element;
10077   int i;
10078
10079   if (!change->has_action)
10080     return;
10081
10082   // ---------- determine action paramater values -----------------------------
10083
10084   int level_time_value =
10085     (level.time > 0 ? TimeLeft :
10086      TimePlayed);
10087
10088   int action_arg_element_raw =
10089     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
10090      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10091      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
10092      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
10093      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10094      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
10095      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
10096      EL_EMPTY);
10097   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10098
10099   int action_arg_direction =
10100     (action_arg >= CA_ARG_DIRECTION_LEFT &&
10101      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10102      action_arg == CA_ARG_DIRECTION_TRIGGER ?
10103      change->actual_trigger_side :
10104      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10105      MV_DIR_OPPOSITE(change->actual_trigger_side) :
10106      MV_NONE);
10107
10108   int action_arg_number_min =
10109     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10110      CA_ARG_MIN);
10111
10112   int action_arg_number_max =
10113     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10114      action_type == CA_SET_LEVEL_GEMS ? 999 :
10115      action_type == CA_SET_LEVEL_TIME ? 9999 :
10116      action_type == CA_SET_LEVEL_SCORE ? 99999 :
10117      action_type == CA_SET_CE_VALUE ? 9999 :
10118      action_type == CA_SET_CE_SCORE ? 9999 :
10119      CA_ARG_MAX);
10120
10121   int action_arg_number_reset =
10122     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10123      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10124      action_type == CA_SET_LEVEL_TIME ? level.time :
10125      action_type == CA_SET_LEVEL_SCORE ? 0 :
10126      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10127      action_type == CA_SET_CE_SCORE ? 0 :
10128      0);
10129
10130   int action_arg_number =
10131     (action_arg <= CA_ARG_MAX ? action_arg :
10132      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10133      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10134      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10135      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10136      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10137      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10138      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10139      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10140      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10141      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10142      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? game.gems_still_needed :
10143      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? game.score :
10144      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10145      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10146      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10147      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10148      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10149      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10150      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
10151      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10152      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
10153      -1);
10154
10155   int action_arg_number_old =
10156     (action_type == CA_SET_LEVEL_GEMS ? game.gems_still_needed :
10157      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10158      action_type == CA_SET_LEVEL_SCORE ? game.score :
10159      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10160      action_type == CA_SET_CE_SCORE ? ei->collect_score :
10161      0);
10162
10163   int action_arg_number_new =
10164     getModifiedActionNumber(action_arg_number_old,
10165                             action_mode, action_arg_number,
10166                             action_arg_number_min, action_arg_number_max);
10167
10168   int trigger_player_bits =
10169     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10170      change->actual_trigger_player_bits : change->trigger_player);
10171
10172   int action_arg_player_bits =
10173     (action_arg >= CA_ARG_PLAYER_1 &&
10174      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10175      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10176      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10177      PLAYER_BITS_ANY);
10178
10179   // ---------- execute action  -----------------------------------------------
10180
10181   switch (action_type)
10182   {
10183     case CA_NO_ACTION:
10184     {
10185       return;
10186     }
10187
10188     // ---------- level actions  ----------------------------------------------
10189
10190     case CA_RESTART_LEVEL:
10191     {
10192       game.restart_level = TRUE;
10193
10194       break;
10195     }
10196
10197     case CA_SHOW_ENVELOPE:
10198     {
10199       int element = getSpecialActionElement(action_arg_element,
10200                                             action_arg_number, EL_ENVELOPE_1);
10201
10202       if (IS_ENVELOPE(element))
10203         local_player->show_envelope = element;
10204
10205       break;
10206     }
10207
10208     case CA_SET_LEVEL_TIME:
10209     {
10210       if (level.time > 0)       // only modify limited time value
10211       {
10212         TimeLeft = action_arg_number_new;
10213
10214         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10215
10216         DisplayGameControlValues();
10217
10218         if (!TimeLeft && game.time_limit)
10219           for (i = 0; i < MAX_PLAYERS; i++)
10220             KillPlayer(&stored_player[i]);
10221       }
10222
10223       break;
10224     }
10225
10226     case CA_SET_LEVEL_SCORE:
10227     {
10228       game.score = action_arg_number_new;
10229
10230       game_panel_controls[GAME_PANEL_SCORE].value = game.score;
10231
10232       DisplayGameControlValues();
10233
10234       break;
10235     }
10236
10237     case CA_SET_LEVEL_GEMS:
10238     {
10239       game.gems_still_needed = action_arg_number_new;
10240
10241       game.snapshot.collected_item = TRUE;
10242
10243       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
10244
10245       DisplayGameControlValues();
10246
10247       break;
10248     }
10249
10250     case CA_SET_LEVEL_WIND:
10251     {
10252       game.wind_direction = action_arg_direction;
10253
10254       break;
10255     }
10256
10257     case CA_SET_LEVEL_RANDOM_SEED:
10258     {
10259       // ensure that setting a new random seed while playing is predictable
10260       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10261
10262       break;
10263     }
10264
10265     // ---------- player actions  ---------------------------------------------
10266
10267     case CA_MOVE_PLAYER:
10268     case CA_MOVE_PLAYER_NEW:
10269     {
10270       // automatically move to the next field in specified direction
10271       for (i = 0; i < MAX_PLAYERS; i++)
10272         if (trigger_player_bits & (1 << i))
10273           if (action_type == CA_MOVE_PLAYER ||
10274               stored_player[i].MovPos == 0)
10275             stored_player[i].programmed_action = action_arg_direction;
10276
10277       break;
10278     }
10279
10280     case CA_EXIT_PLAYER:
10281     {
10282       for (i = 0; i < MAX_PLAYERS; i++)
10283         if (action_arg_player_bits & (1 << i))
10284           ExitPlayer(&stored_player[i]);
10285
10286       if (game.players_still_needed == 0)
10287         LevelSolved();
10288
10289       break;
10290     }
10291
10292     case CA_KILL_PLAYER:
10293     {
10294       for (i = 0; i < MAX_PLAYERS; i++)
10295         if (action_arg_player_bits & (1 << i))
10296           KillPlayer(&stored_player[i]);
10297
10298       break;
10299     }
10300
10301     case CA_SET_PLAYER_KEYS:
10302     {
10303       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10304       int element = getSpecialActionElement(action_arg_element,
10305                                             action_arg_number, EL_KEY_1);
10306
10307       if (IS_KEY(element))
10308       {
10309         for (i = 0; i < MAX_PLAYERS; i++)
10310         {
10311           if (trigger_player_bits & (1 << i))
10312           {
10313             stored_player[i].key[KEY_NR(element)] = key_state;
10314
10315             DrawGameDoorValues();
10316           }
10317         }
10318       }
10319
10320       break;
10321     }
10322
10323     case CA_SET_PLAYER_SPEED:
10324     {
10325       for (i = 0; i < MAX_PLAYERS; i++)
10326       {
10327         if (trigger_player_bits & (1 << i))
10328         {
10329           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10330
10331           if (action_arg == CA_ARG_SPEED_FASTER &&
10332               stored_player[i].cannot_move)
10333           {
10334             action_arg_number = STEPSIZE_VERY_SLOW;
10335           }
10336           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10337                    action_arg == CA_ARG_SPEED_FASTER)
10338           {
10339             action_arg_number = 2;
10340             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10341                            CA_MODE_MULTIPLY);
10342           }
10343           else if (action_arg == CA_ARG_NUMBER_RESET)
10344           {
10345             action_arg_number = level.initial_player_stepsize[i];
10346           }
10347
10348           move_stepsize =
10349             getModifiedActionNumber(move_stepsize,
10350                                     action_mode,
10351                                     action_arg_number,
10352                                     action_arg_number_min,
10353                                     action_arg_number_max);
10354
10355           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10356         }
10357       }
10358
10359       break;
10360     }
10361
10362     case CA_SET_PLAYER_SHIELD:
10363     {
10364       for (i = 0; i < MAX_PLAYERS; i++)
10365       {
10366         if (trigger_player_bits & (1 << i))
10367         {
10368           if (action_arg == CA_ARG_SHIELD_OFF)
10369           {
10370             stored_player[i].shield_normal_time_left = 0;
10371             stored_player[i].shield_deadly_time_left = 0;
10372           }
10373           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10374           {
10375             stored_player[i].shield_normal_time_left = 999999;
10376           }
10377           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10378           {
10379             stored_player[i].shield_normal_time_left = 999999;
10380             stored_player[i].shield_deadly_time_left = 999999;
10381           }
10382         }
10383       }
10384
10385       break;
10386     }
10387
10388     case CA_SET_PLAYER_GRAVITY:
10389     {
10390       for (i = 0; i < MAX_PLAYERS; i++)
10391       {
10392         if (trigger_player_bits & (1 << i))
10393         {
10394           stored_player[i].gravity =
10395             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10396              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10397              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10398              stored_player[i].gravity);
10399         }
10400       }
10401
10402       break;
10403     }
10404
10405     case CA_SET_PLAYER_ARTWORK:
10406     {
10407       for (i = 0; i < MAX_PLAYERS; i++)
10408       {
10409         if (trigger_player_bits & (1 << i))
10410         {
10411           int artwork_element = action_arg_element;
10412
10413           if (action_arg == CA_ARG_ELEMENT_RESET)
10414             artwork_element =
10415               (level.use_artwork_element[i] ? level.artwork_element[i] :
10416                stored_player[i].element_nr);
10417
10418           if (stored_player[i].artwork_element != artwork_element)
10419             stored_player[i].Frame = 0;
10420
10421           stored_player[i].artwork_element = artwork_element;
10422
10423           SetPlayerWaiting(&stored_player[i], FALSE);
10424
10425           // set number of special actions for bored and sleeping animation
10426           stored_player[i].num_special_action_bored =
10427             get_num_special_action(artwork_element,
10428                                    ACTION_BORING_1, ACTION_BORING_LAST);
10429           stored_player[i].num_special_action_sleeping =
10430             get_num_special_action(artwork_element,
10431                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10432         }
10433       }
10434
10435       break;
10436     }
10437
10438     case CA_SET_PLAYER_INVENTORY:
10439     {
10440       for (i = 0; i < MAX_PLAYERS; i++)
10441       {
10442         struct PlayerInfo *player = &stored_player[i];
10443         int j, k;
10444
10445         if (trigger_player_bits & (1 << i))
10446         {
10447           int inventory_element = action_arg_element;
10448
10449           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10450               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10451               action_arg == CA_ARG_ELEMENT_ACTION)
10452           {
10453             int element = inventory_element;
10454             int collect_count = element_info[element].collect_count_initial;
10455
10456             if (!IS_CUSTOM_ELEMENT(element))
10457               collect_count = 1;
10458
10459             if (collect_count == 0)
10460               player->inventory_infinite_element = element;
10461             else
10462               for (k = 0; k < collect_count; k++)
10463                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10464                   player->inventory_element[player->inventory_size++] =
10465                     element;
10466           }
10467           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10468                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10469                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10470           {
10471             if (player->inventory_infinite_element != EL_UNDEFINED &&
10472                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10473                                      action_arg_element_raw))
10474               player->inventory_infinite_element = EL_UNDEFINED;
10475
10476             for (k = 0, j = 0; j < player->inventory_size; j++)
10477             {
10478               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10479                                         action_arg_element_raw))
10480                 player->inventory_element[k++] = player->inventory_element[j];
10481             }
10482
10483             player->inventory_size = k;
10484           }
10485           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10486           {
10487             if (player->inventory_size > 0)
10488             {
10489               for (j = 0; j < player->inventory_size - 1; j++)
10490                 player->inventory_element[j] = player->inventory_element[j + 1];
10491
10492               player->inventory_size--;
10493             }
10494           }
10495           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10496           {
10497             if (player->inventory_size > 0)
10498               player->inventory_size--;
10499           }
10500           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10501           {
10502             player->inventory_infinite_element = EL_UNDEFINED;
10503             player->inventory_size = 0;
10504           }
10505           else if (action_arg == CA_ARG_INVENTORY_RESET)
10506           {
10507             player->inventory_infinite_element = EL_UNDEFINED;
10508             player->inventory_size = 0;
10509
10510             if (level.use_initial_inventory[i])
10511             {
10512               for (j = 0; j < level.initial_inventory_size[i]; j++)
10513               {
10514                 int element = level.initial_inventory_content[i][j];
10515                 int collect_count = element_info[element].collect_count_initial;
10516
10517                 if (!IS_CUSTOM_ELEMENT(element))
10518                   collect_count = 1;
10519
10520                 if (collect_count == 0)
10521                   player->inventory_infinite_element = element;
10522                 else
10523                   for (k = 0; k < collect_count; k++)
10524                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10525                       player->inventory_element[player->inventory_size++] =
10526                         element;
10527               }
10528             }
10529           }
10530         }
10531       }
10532
10533       break;
10534     }
10535
10536     // ---------- CE actions  -------------------------------------------------
10537
10538     case CA_SET_CE_VALUE:
10539     {
10540       int last_ce_value = CustomValue[x][y];
10541
10542       CustomValue[x][y] = action_arg_number_new;
10543
10544       if (CustomValue[x][y] != last_ce_value)
10545       {
10546         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10547         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10548
10549         if (CustomValue[x][y] == 0)
10550         {
10551           // reset change counter (else CE_VALUE_GETS_ZERO would not work)
10552           ChangeCount[x][y] = 0;        // allow at least one more change
10553
10554           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10555           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10556         }
10557       }
10558
10559       break;
10560     }
10561
10562     case CA_SET_CE_SCORE:
10563     {
10564       int last_ce_score = ei->collect_score;
10565
10566       ei->collect_score = action_arg_number_new;
10567
10568       if (ei->collect_score != last_ce_score)
10569       {
10570         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10571         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10572
10573         if (ei->collect_score == 0)
10574         {
10575           int xx, yy;
10576
10577           // reset change counter (else CE_SCORE_GETS_ZERO would not work)
10578           ChangeCount[x][y] = 0;        // allow at least one more change
10579
10580           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10581           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10582
10583           /*
10584             This is a very special case that seems to be a mixture between
10585             CheckElementChange() and CheckTriggeredElementChange(): while
10586             the first one only affects single elements that are triggered
10587             directly, the second one affects multiple elements in the playfield
10588             that are triggered indirectly by another element. This is a third
10589             case: Changing the CE score always affects multiple identical CEs,
10590             so every affected CE must be checked, not only the single CE for
10591             which the CE score was changed in the first place (as every instance
10592             of that CE shares the same CE score, and therefore also can change)!
10593           */
10594           SCAN_PLAYFIELD(xx, yy)
10595           {
10596             if (Tile[xx][yy] == element)
10597               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10598                                  CE_SCORE_GETS_ZERO);
10599           }
10600         }
10601       }
10602
10603       break;
10604     }
10605
10606     case CA_SET_CE_ARTWORK:
10607     {
10608       int artwork_element = action_arg_element;
10609       boolean reset_frame = FALSE;
10610       int xx, yy;
10611
10612       if (action_arg == CA_ARG_ELEMENT_RESET)
10613         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10614                            element);
10615
10616       if (ei->gfx_element != artwork_element)
10617         reset_frame = TRUE;
10618
10619       ei->gfx_element = artwork_element;
10620
10621       SCAN_PLAYFIELD(xx, yy)
10622       {
10623         if (Tile[xx][yy] == element)
10624         {
10625           if (reset_frame)
10626           {
10627             ResetGfxAnimation(xx, yy);
10628             ResetRandomAnimationValue(xx, yy);
10629           }
10630
10631           TEST_DrawLevelField(xx, yy);
10632         }
10633       }
10634
10635       break;
10636     }
10637
10638     // ---------- engine actions  ---------------------------------------------
10639
10640     case CA_SET_ENGINE_SCAN_MODE:
10641     {
10642       InitPlayfieldScanMode(action_arg);
10643
10644       break;
10645     }
10646
10647     default:
10648       break;
10649   }
10650 }
10651
10652 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10653 {
10654   int old_element = Tile[x][y];
10655   int new_element = GetElementFromGroupElement(element);
10656   int previous_move_direction = MovDir[x][y];
10657   int last_ce_value = CustomValue[x][y];
10658   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10659   boolean new_element_is_player = IS_PLAYER_ELEMENT(new_element);
10660   boolean add_player_onto_element = (new_element_is_player &&
10661                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10662                                      IS_WALKABLE(old_element));
10663
10664   if (!add_player_onto_element)
10665   {
10666     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10667       RemoveMovingField(x, y);
10668     else
10669       RemoveField(x, y);
10670
10671     Tile[x][y] = new_element;
10672
10673     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10674       MovDir[x][y] = previous_move_direction;
10675
10676     if (element_info[new_element].use_last_ce_value)
10677       CustomValue[x][y] = last_ce_value;
10678
10679     InitField_WithBug1(x, y, FALSE);
10680
10681     new_element = Tile[x][y];   // element may have changed
10682
10683     ResetGfxAnimation(x, y);
10684     ResetRandomAnimationValue(x, y);
10685
10686     TEST_DrawLevelField(x, y);
10687
10688     if (GFX_CRUMBLED(new_element))
10689       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10690
10691     if (old_element == EL_EXPLOSION)
10692     {
10693       Store[x][y] = Store2[x][y] = 0;
10694
10695       // check if new element replaces an exploding player, requiring cleanup
10696       if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
10697         StorePlayer[x][y] = 0;
10698     }
10699
10700     // check if element under the player changes from accessible to unaccessible
10701     // (needed for special case of dropping element which then changes)
10702     // (must be checked after creating new element for walkable group elements)
10703     if (IS_PLAYER(x, y) && !player_explosion_protected &&
10704         IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10705     {
10706       KillPlayer(PLAYERINFO(x, y));
10707
10708       return;
10709     }
10710   }
10711
10712   // "ChangeCount" not set yet to allow "entered by player" change one time
10713   if (new_element_is_player)
10714     RelocatePlayer(x, y, new_element);
10715
10716   if (is_change)
10717     ChangeCount[x][y]++;        // count number of changes in the same frame
10718
10719   TestIfBadThingTouchesPlayer(x, y);
10720   TestIfPlayerTouchesCustomElement(x, y);
10721   TestIfElementTouchesCustomElement(x, y);
10722 }
10723
10724 static void CreateField(int x, int y, int element)
10725 {
10726   CreateFieldExt(x, y, element, FALSE);
10727 }
10728
10729 static void CreateElementFromChange(int x, int y, int element)
10730 {
10731   element = GET_VALID_RUNTIME_ELEMENT(element);
10732
10733   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10734   {
10735     int old_element = Tile[x][y];
10736
10737     // prevent changed element from moving in same engine frame
10738     // unless both old and new element can either fall or move
10739     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10740         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10741       Stop[x][y] = TRUE;
10742   }
10743
10744   CreateFieldExt(x, y, element, TRUE);
10745 }
10746
10747 static boolean ChangeElement(int x, int y, int element, int page)
10748 {
10749   struct ElementInfo *ei = &element_info[element];
10750   struct ElementChangeInfo *change = &ei->change_page[page];
10751   int ce_value = CustomValue[x][y];
10752   int ce_score = ei->collect_score;
10753   int target_element;
10754   int old_element = Tile[x][y];
10755
10756   // always use default change event to prevent running into a loop
10757   if (ChangeEvent[x][y] == -1)
10758     ChangeEvent[x][y] = CE_DELAY;
10759
10760   if (ChangeEvent[x][y] == CE_DELAY)
10761   {
10762     // reset actual trigger element, trigger player and action element
10763     change->actual_trigger_element = EL_EMPTY;
10764     change->actual_trigger_player = EL_EMPTY;
10765     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10766     change->actual_trigger_side = CH_SIDE_NONE;
10767     change->actual_trigger_ce_value = 0;
10768     change->actual_trigger_ce_score = 0;
10769     change->actual_trigger_x = -1;
10770     change->actual_trigger_y = -1;
10771   }
10772
10773   // do not change elements more than a specified maximum number of changes
10774   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10775     return FALSE;
10776
10777   ChangeCount[x][y]++;          // count number of changes in the same frame
10778
10779   if (ei->has_anim_event)
10780     HandleGlobalAnimEventByElementChange(element, page, x, y,
10781                                          change->actual_trigger_x,
10782                                          change->actual_trigger_y);
10783
10784   if (change->explode)
10785   {
10786     Bang(x, y);
10787
10788     return TRUE;
10789   }
10790
10791   if (change->use_target_content)
10792   {
10793     boolean complete_replace = TRUE;
10794     boolean can_replace[3][3];
10795     int xx, yy;
10796
10797     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10798     {
10799       boolean is_empty;
10800       boolean is_walkable;
10801       boolean is_diggable;
10802       boolean is_collectible;
10803       boolean is_removable;
10804       boolean is_destructible;
10805       int ex = x + xx - 1;
10806       int ey = y + yy - 1;
10807       int content_element = change->target_content.e[xx][yy];
10808       int e;
10809
10810       can_replace[xx][yy] = TRUE;
10811
10812       if (ex == x && ey == y)   // do not check changing element itself
10813         continue;
10814
10815       if (content_element == EL_EMPTY_SPACE)
10816       {
10817         can_replace[xx][yy] = FALSE;    // do not replace border with space
10818
10819         continue;
10820       }
10821
10822       if (!IN_LEV_FIELD(ex, ey))
10823       {
10824         can_replace[xx][yy] = FALSE;
10825         complete_replace = FALSE;
10826
10827         continue;
10828       }
10829
10830       e = Tile[ex][ey];
10831
10832       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10833         e = MovingOrBlocked2Element(ex, ey);
10834
10835       is_empty = (IS_FREE(ex, ey) ||
10836                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10837
10838       is_walkable     = (is_empty || IS_WALKABLE(e));
10839       is_diggable     = (is_empty || IS_DIGGABLE(e));
10840       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10841       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10842       is_removable    = (is_diggable || is_collectible);
10843
10844       can_replace[xx][yy] =
10845         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10846           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10847           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10848           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10849           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10850           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10851          !(IS_PLAYER(ex, ey) && IS_PLAYER_ELEMENT(content_element)));
10852
10853       if (!can_replace[xx][yy])
10854         complete_replace = FALSE;
10855     }
10856
10857     if (!change->only_if_complete || complete_replace)
10858     {
10859       boolean something_has_changed = FALSE;
10860
10861       if (change->only_if_complete && change->use_random_replace &&
10862           RND(100) < change->random_percentage)
10863         return FALSE;
10864
10865       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10866       {
10867         int ex = x + xx - 1;
10868         int ey = y + yy - 1;
10869         int content_element;
10870
10871         if (can_replace[xx][yy] && (!change->use_random_replace ||
10872                                     RND(100) < change->random_percentage))
10873         {
10874           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10875             RemoveMovingField(ex, ey);
10876
10877           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10878
10879           content_element = change->target_content.e[xx][yy];
10880           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10881                                               ce_value, ce_score);
10882
10883           CreateElementFromChange(ex, ey, target_element);
10884
10885           something_has_changed = TRUE;
10886
10887           // for symmetry reasons, freeze newly created border elements
10888           if (ex != x || ey != y)
10889             Stop[ex][ey] = TRUE;        // no more moving in this frame
10890         }
10891       }
10892
10893       if (something_has_changed)
10894       {
10895         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10896         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10897       }
10898     }
10899   }
10900   else
10901   {
10902     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10903                                         ce_value, ce_score);
10904
10905     if (element == EL_DIAGONAL_GROWING ||
10906         element == EL_DIAGONAL_SHRINKING)
10907     {
10908       target_element = Store[x][y];
10909
10910       Store[x][y] = EL_EMPTY;
10911     }
10912
10913     // special case: element changes to player (and may be kept if walkable)
10914     if (IS_PLAYER_ELEMENT(target_element) && !level.keep_walkable_ce)
10915       CreateElementFromChange(x, y, EL_EMPTY);
10916
10917     CreateElementFromChange(x, y, target_element);
10918
10919     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10920     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10921   }
10922
10923   // this uses direct change before indirect change
10924   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10925
10926   return TRUE;
10927 }
10928
10929 static void HandleElementChange(int x, int y, int page)
10930 {
10931   int element = MovingOrBlocked2Element(x, y);
10932   struct ElementInfo *ei = &element_info[element];
10933   struct ElementChangeInfo *change = &ei->change_page[page];
10934   boolean handle_action_before_change = FALSE;
10935
10936 #ifdef DEBUG
10937   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10938       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10939   {
10940     Debug("game:playing:HandleElementChange", "%d,%d: element = %d ('%s')",
10941           x, y, element, element_info[element].token_name);
10942     Debug("game:playing:HandleElementChange", "This should never happen!");
10943   }
10944 #endif
10945
10946   // this can happen with classic bombs on walkable, changing elements
10947   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10948   {
10949     return;
10950   }
10951
10952   if (ChangeDelay[x][y] == 0)           // initialize element change
10953   {
10954     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10955
10956     if (change->can_change)
10957     {
10958       // !!! not clear why graphic animation should be reset at all here !!!
10959       // !!! UPDATE: but is needed for correct Snake Bite tail animation !!!
10960       // !!! SOLUTION: do not reset if graphics engine set to 4 or above !!!
10961
10962       /*
10963         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10964
10965         When using an animation frame delay of 1 (this only happens with
10966         "sp_zonk.moving.left/right" in the classic graphics), the default
10967         (non-moving) animation shows wrong animation frames (while the
10968         moving animation, like "sp_zonk.moving.left/right", is correct,
10969         so this graphical bug never shows up with the classic graphics).
10970         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10971         be drawn instead of the correct frames 0,1,2,3. This is caused by
10972         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10973         an element change: First when the change delay ("ChangeDelay[][]")
10974         counter has reached zero after decrementing, then a second time in
10975         the next frame (after "GfxFrame[][]" was already incremented) when
10976         "ChangeDelay[][]" is reset to the initial delay value again.
10977
10978         This causes frame 0 to be drawn twice, while the last frame won't
10979         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10980
10981         As some animations may already be cleverly designed around this bug
10982         (at least the "Snake Bite" snake tail animation does this), it cannot
10983         simply be fixed here without breaking such existing animations.
10984         Unfortunately, it cannot easily be detected if a graphics set was
10985         designed "before" or "after" the bug was fixed. As a workaround,
10986         a new graphics set option "game.graphics_engine_version" was added
10987         to be able to specify the game's major release version for which the
10988         graphics set was designed, which can then be used to decide if the
10989         bugfix should be used (version 4 and above) or not (version 3 or
10990         below, or if no version was specified at all, as with old sets).
10991
10992         (The wrong/fixed animation frames can be tested with the test level set
10993         "test_gfxframe" and level "000", which contains a specially prepared
10994         custom element at level position (x/y) == (11/9) which uses the zonk
10995         animation mentioned above. Using "game.graphics_engine_version: 4"
10996         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10997         This can also be seen from the debug output for this test element.)
10998       */
10999
11000       // when a custom element is about to change (for example by change delay),
11001       // do not reset graphic animation when the custom element is moving
11002       if (game.graphics_engine_version < 4 &&
11003           !IS_MOVING(x, y))
11004       {
11005         ResetGfxAnimation(x, y);
11006         ResetRandomAnimationValue(x, y);
11007       }
11008
11009       if (change->pre_change_function)
11010         change->pre_change_function(x, y);
11011     }
11012   }
11013
11014   ChangeDelay[x][y]--;
11015
11016   if (ChangeDelay[x][y] != 0)           // continue element change
11017   {
11018     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11019
11020     // also needed if CE can not change, but has CE delay with CE action
11021     if (IS_ANIMATED(graphic))
11022       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11023
11024     if (change->can_change)
11025     {
11026       if (change->change_function)
11027         change->change_function(x, y);
11028     }
11029   }
11030   else                                  // finish element change
11031   {
11032     if (ChangePage[x][y] != -1)         // remember page from delayed change
11033     {
11034       page = ChangePage[x][y];
11035       ChangePage[x][y] = -1;
11036
11037       change = &ei->change_page[page];
11038     }
11039
11040     if (IS_MOVING(x, y))                // never change a running system ;-)
11041     {
11042       ChangeDelay[x][y] = 1;            // try change after next move step
11043       ChangePage[x][y] = page;          // remember page to use for change
11044
11045       return;
11046     }
11047
11048     // special case: set new level random seed before changing element
11049     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
11050       handle_action_before_change = TRUE;
11051
11052     if (change->has_action && handle_action_before_change)
11053       ExecuteCustomElementAction(x, y, element, page);
11054
11055     if (change->can_change)
11056     {
11057       if (ChangeElement(x, y, element, page))
11058       {
11059         if (change->post_change_function)
11060           change->post_change_function(x, y);
11061       }
11062     }
11063
11064     if (change->has_action && !handle_action_before_change)
11065       ExecuteCustomElementAction(x, y, element, page);
11066   }
11067 }
11068
11069 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11070                                               int trigger_element,
11071                                               int trigger_event,
11072                                               int trigger_player,
11073                                               int trigger_side,
11074                                               int trigger_page)
11075 {
11076   boolean change_done_any = FALSE;
11077   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11078   int i;
11079
11080   if (!(trigger_events[trigger_element][trigger_event]))
11081     return FALSE;
11082
11083   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11084
11085   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11086   {
11087     int element = EL_CUSTOM_START + i;
11088     boolean change_done = FALSE;
11089     int p;
11090
11091     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11092         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11093       continue;
11094
11095     for (p = 0; p < element_info[element].num_change_pages; p++)
11096     {
11097       struct ElementChangeInfo *change = &element_info[element].change_page[p];
11098
11099       if (change->can_change_or_has_action &&
11100           change->has_event[trigger_event] &&
11101           change->trigger_side & trigger_side &&
11102           change->trigger_player & trigger_player &&
11103           change->trigger_page & trigger_page_bits &&
11104           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11105       {
11106         change->actual_trigger_element = trigger_element;
11107         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11108         change->actual_trigger_player_bits = trigger_player;
11109         change->actual_trigger_side = trigger_side;
11110         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11111         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11112         change->actual_trigger_x = trigger_x;
11113         change->actual_trigger_y = trigger_y;
11114
11115         if ((change->can_change && !change_done) || change->has_action)
11116         {
11117           int x, y;
11118
11119           SCAN_PLAYFIELD(x, y)
11120           {
11121             if (Tile[x][y] == element)
11122             {
11123               if (change->can_change && !change_done)
11124               {
11125                 // if element already changed in this frame, not only prevent
11126                 // another element change (checked in ChangeElement()), but
11127                 // also prevent additional element actions for this element
11128
11129                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11130                     !level.use_action_after_change_bug)
11131                   continue;
11132
11133                 ChangeDelay[x][y] = 1;
11134                 ChangeEvent[x][y] = trigger_event;
11135
11136                 HandleElementChange(x, y, p);
11137               }
11138               else if (change->has_action)
11139               {
11140                 // if element already changed in this frame, not only prevent
11141                 // another element change (checked in ChangeElement()), but
11142                 // also prevent additional element actions for this element
11143
11144                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11145                     !level.use_action_after_change_bug)
11146                   continue;
11147
11148                 ExecuteCustomElementAction(x, y, element, p);
11149                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11150               }
11151             }
11152           }
11153
11154           if (change->can_change)
11155           {
11156             change_done = TRUE;
11157             change_done_any = TRUE;
11158           }
11159         }
11160       }
11161     }
11162   }
11163
11164   RECURSION_LOOP_DETECTION_END();
11165
11166   return change_done_any;
11167 }
11168
11169 static boolean CheckElementChangeExt(int x, int y,
11170                                      int element,
11171                                      int trigger_element,
11172                                      int trigger_event,
11173                                      int trigger_player,
11174                                      int trigger_side)
11175 {
11176   boolean change_done = FALSE;
11177   int p;
11178
11179   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11180       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11181     return FALSE;
11182
11183   if (Tile[x][y] == EL_BLOCKED)
11184   {
11185     Blocked2Moving(x, y, &x, &y);
11186     element = Tile[x][y];
11187   }
11188
11189   // check if element has already changed or is about to change after moving
11190   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11191        Tile[x][y] != element) ||
11192
11193       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11194        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11195         ChangePage[x][y] != -1)))
11196     return FALSE;
11197
11198   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11199
11200   for (p = 0; p < element_info[element].num_change_pages; p++)
11201   {
11202     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11203
11204     /* check trigger element for all events where the element that is checked
11205        for changing interacts with a directly adjacent element -- this is
11206        different to element changes that affect other elements to change on the
11207        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11208     boolean check_trigger_element =
11209       (trigger_event == CE_NEXT_TO_X ||
11210        trigger_event == CE_TOUCHING_X ||
11211        trigger_event == CE_HITTING_X ||
11212        trigger_event == CE_HIT_BY_X ||
11213        trigger_event == CE_DIGGING_X); // this one was forgotten until 3.2.3
11214
11215     if (change->can_change_or_has_action &&
11216         change->has_event[trigger_event] &&
11217         change->trigger_side & trigger_side &&
11218         change->trigger_player & trigger_player &&
11219         (!check_trigger_element ||
11220          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11221     {
11222       change->actual_trigger_element = trigger_element;
11223       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11224       change->actual_trigger_player_bits = trigger_player;
11225       change->actual_trigger_side = trigger_side;
11226       change->actual_trigger_ce_value = CustomValue[x][y];
11227       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11228       change->actual_trigger_x = x;
11229       change->actual_trigger_y = y;
11230
11231       // special case: trigger element not at (x,y) position for some events
11232       if (check_trigger_element)
11233       {
11234         static struct
11235         {
11236           int dx, dy;
11237         } move_xy[] =
11238           {
11239             {  0,  0 },
11240             { -1,  0 },
11241             { +1,  0 },
11242             {  0,  0 },
11243             {  0, -1 },
11244             {  0,  0 }, { 0, 0 }, { 0, 0 },
11245             {  0, +1 }
11246           };
11247
11248         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11249         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11250
11251         change->actual_trigger_ce_value = CustomValue[xx][yy];
11252         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11253         change->actual_trigger_x = xx;
11254         change->actual_trigger_y = yy;
11255       }
11256
11257       if (change->can_change && !change_done)
11258       {
11259         ChangeDelay[x][y] = 1;
11260         ChangeEvent[x][y] = trigger_event;
11261
11262         HandleElementChange(x, y, p);
11263
11264         change_done = TRUE;
11265       }
11266       else if (change->has_action)
11267       {
11268         ExecuteCustomElementAction(x, y, element, p);
11269         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11270       }
11271     }
11272   }
11273
11274   RECURSION_LOOP_DETECTION_END();
11275
11276   return change_done;
11277 }
11278
11279 static void PlayPlayerSound(struct PlayerInfo *player)
11280 {
11281   int jx = player->jx, jy = player->jy;
11282   int sound_element = player->artwork_element;
11283   int last_action = player->last_action_waiting;
11284   int action = player->action_waiting;
11285
11286   if (player->is_waiting)
11287   {
11288     if (action != last_action)
11289       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11290     else
11291       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11292   }
11293   else
11294   {
11295     if (action != last_action)
11296       StopSound(element_info[sound_element].sound[last_action]);
11297
11298     if (last_action == ACTION_SLEEPING)
11299       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11300   }
11301 }
11302
11303 static void PlayAllPlayersSound(void)
11304 {
11305   int i;
11306
11307   for (i = 0; i < MAX_PLAYERS; i++)
11308     if (stored_player[i].active)
11309       PlayPlayerSound(&stored_player[i]);
11310 }
11311
11312 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11313 {
11314   boolean last_waiting = player->is_waiting;
11315   int move_dir = player->MovDir;
11316
11317   player->dir_waiting = move_dir;
11318   player->last_action_waiting = player->action_waiting;
11319
11320   if (is_waiting)
11321   {
11322     if (!last_waiting)          // not waiting -> waiting
11323     {
11324       player->is_waiting = TRUE;
11325
11326       player->frame_counter_bored =
11327         FrameCounter +
11328         game.player_boring_delay_fixed +
11329         GetSimpleRandom(game.player_boring_delay_random);
11330       player->frame_counter_sleeping =
11331         FrameCounter +
11332         game.player_sleeping_delay_fixed +
11333         GetSimpleRandom(game.player_sleeping_delay_random);
11334
11335       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11336     }
11337
11338     if (game.player_sleeping_delay_fixed +
11339         game.player_sleeping_delay_random > 0 &&
11340         player->anim_delay_counter == 0 &&
11341         player->post_delay_counter == 0 &&
11342         FrameCounter >= player->frame_counter_sleeping)
11343       player->is_sleeping = TRUE;
11344     else if (game.player_boring_delay_fixed +
11345              game.player_boring_delay_random > 0 &&
11346              FrameCounter >= player->frame_counter_bored)
11347       player->is_bored = TRUE;
11348
11349     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11350                               player->is_bored ? ACTION_BORING :
11351                               ACTION_WAITING);
11352
11353     if (player->is_sleeping && player->use_murphy)
11354     {
11355       // special case for sleeping Murphy when leaning against non-free tile
11356
11357       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11358           (Tile[player->jx - 1][player->jy] != EL_EMPTY &&
11359            !IS_MOVING(player->jx - 1, player->jy)))
11360         move_dir = MV_LEFT;
11361       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11362                (Tile[player->jx + 1][player->jy] != EL_EMPTY &&
11363                 !IS_MOVING(player->jx + 1, player->jy)))
11364         move_dir = MV_RIGHT;
11365       else
11366         player->is_sleeping = FALSE;
11367
11368       player->dir_waiting = move_dir;
11369     }
11370
11371     if (player->is_sleeping)
11372     {
11373       if (player->num_special_action_sleeping > 0)
11374       {
11375         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11376         {
11377           int last_special_action = player->special_action_sleeping;
11378           int num_special_action = player->num_special_action_sleeping;
11379           int special_action =
11380             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11381              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11382              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11383              last_special_action + 1 : ACTION_SLEEPING);
11384           int special_graphic =
11385             el_act_dir2img(player->artwork_element, special_action, move_dir);
11386
11387           player->anim_delay_counter =
11388             graphic_info[special_graphic].anim_delay_fixed +
11389             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11390           player->post_delay_counter =
11391             graphic_info[special_graphic].post_delay_fixed +
11392             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11393
11394           player->special_action_sleeping = special_action;
11395         }
11396
11397         if (player->anim_delay_counter > 0)
11398         {
11399           player->action_waiting = player->special_action_sleeping;
11400           player->anim_delay_counter--;
11401         }
11402         else if (player->post_delay_counter > 0)
11403         {
11404           player->post_delay_counter--;
11405         }
11406       }
11407     }
11408     else if (player->is_bored)
11409     {
11410       if (player->num_special_action_bored > 0)
11411       {
11412         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11413         {
11414           int special_action =
11415             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11416           int special_graphic =
11417             el_act_dir2img(player->artwork_element, special_action, move_dir);
11418
11419           player->anim_delay_counter =
11420             graphic_info[special_graphic].anim_delay_fixed +
11421             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11422           player->post_delay_counter =
11423             graphic_info[special_graphic].post_delay_fixed +
11424             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11425
11426           player->special_action_bored = special_action;
11427         }
11428
11429         if (player->anim_delay_counter > 0)
11430         {
11431           player->action_waiting = player->special_action_bored;
11432           player->anim_delay_counter--;
11433         }
11434         else if (player->post_delay_counter > 0)
11435         {
11436           player->post_delay_counter--;
11437         }
11438       }
11439     }
11440   }
11441   else if (last_waiting)        // waiting -> not waiting
11442   {
11443     player->is_waiting = FALSE;
11444     player->is_bored = FALSE;
11445     player->is_sleeping = FALSE;
11446
11447     player->frame_counter_bored = -1;
11448     player->frame_counter_sleeping = -1;
11449
11450     player->anim_delay_counter = 0;
11451     player->post_delay_counter = 0;
11452
11453     player->dir_waiting = player->MovDir;
11454     player->action_waiting = ACTION_DEFAULT;
11455
11456     player->special_action_bored = ACTION_DEFAULT;
11457     player->special_action_sleeping = ACTION_DEFAULT;
11458   }
11459 }
11460
11461 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
11462 {
11463   if ((!player->is_moving  && player->was_moving) ||
11464       (player->MovPos == 0 && player->was_moving) ||
11465       (player->is_snapping && !player->was_snapping) ||
11466       (player->is_dropping && !player->was_dropping))
11467   {
11468     if (!CheckSaveEngineSnapshotToList())
11469       return;
11470
11471     player->was_moving = FALSE;
11472     player->was_snapping = TRUE;
11473     player->was_dropping = TRUE;
11474   }
11475   else
11476   {
11477     if (player->is_moving)
11478       player->was_moving = TRUE;
11479
11480     if (!player->is_snapping)
11481       player->was_snapping = FALSE;
11482
11483     if (!player->is_dropping)
11484       player->was_dropping = FALSE;
11485   }
11486
11487   static struct MouseActionInfo mouse_action_last = { 0 };
11488   struct MouseActionInfo mouse_action = player->effective_mouse_action;
11489   boolean new_released = (!mouse_action.button && mouse_action_last.button);
11490
11491   if (new_released)
11492     CheckSaveEngineSnapshotToList();
11493
11494   mouse_action_last = mouse_action;
11495 }
11496
11497 static void CheckSingleStepMode(struct PlayerInfo *player)
11498 {
11499   if (tape.single_step && tape.recording && !tape.pausing)
11500   {
11501     // as it is called "single step mode", just return to pause mode when the
11502     // player stopped moving after one tile (or never starts moving at all)
11503     // (reverse logic needed here in case single step mode used in team mode)
11504     if (player->is_moving ||
11505         player->is_pushing ||
11506         player->is_dropping_pressed ||
11507         player->effective_mouse_action.button)
11508       game.enter_single_step_mode = FALSE;
11509   }
11510
11511   CheckSaveEngineSnapshot(player);
11512 }
11513
11514 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11515 {
11516   int left      = player_action & JOY_LEFT;
11517   int right     = player_action & JOY_RIGHT;
11518   int up        = player_action & JOY_UP;
11519   int down      = player_action & JOY_DOWN;
11520   int button1   = player_action & JOY_BUTTON_1;
11521   int button2   = player_action & JOY_BUTTON_2;
11522   int dx        = (left ? -1 : right ? 1 : 0);
11523   int dy        = (up   ? -1 : down  ? 1 : 0);
11524
11525   if (!player->active || tape.pausing)
11526     return 0;
11527
11528   if (player_action)
11529   {
11530     if (button1)
11531       SnapField(player, dx, dy);
11532     else
11533     {
11534       if (button2)
11535         DropElement(player);
11536
11537       MovePlayer(player, dx, dy);
11538     }
11539
11540     CheckSingleStepMode(player);
11541
11542     SetPlayerWaiting(player, FALSE);
11543
11544     return player_action;
11545   }
11546   else
11547   {
11548     // no actions for this player (no input at player's configured device)
11549
11550     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11551     SnapField(player, 0, 0);
11552     CheckGravityMovementWhenNotMoving(player);
11553
11554     if (player->MovPos == 0)
11555       SetPlayerWaiting(player, TRUE);
11556
11557     if (player->MovPos == 0)    // needed for tape.playing
11558       player->is_moving = FALSE;
11559
11560     player->is_dropping = FALSE;
11561     player->is_dropping_pressed = FALSE;
11562     player->drop_pressed_delay = 0;
11563
11564     CheckSingleStepMode(player);
11565
11566     return 0;
11567   }
11568 }
11569
11570 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11571                                          byte *tape_action)
11572 {
11573   if (!tape.use_mouse_actions)
11574     return;
11575
11576   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11577   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11578   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11579 }
11580
11581 static void SetTapeActionFromMouseAction(byte *tape_action,
11582                                          struct MouseActionInfo *mouse_action)
11583 {
11584   if (!tape.use_mouse_actions)
11585     return;
11586
11587   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11588   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11589   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11590 }
11591
11592 static void CheckLevelSolved(void)
11593 {
11594   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11595   {
11596     if (game_em.level_solved &&
11597         !game_em.game_over)                             // game won
11598     {
11599       LevelSolved();
11600
11601       game_em.game_over = TRUE;
11602
11603       game.all_players_gone = TRUE;
11604     }
11605
11606     if (game_em.game_over)                              // game lost
11607       game.all_players_gone = TRUE;
11608   }
11609   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11610   {
11611     if (game_sp.level_solved &&
11612         !game_sp.game_over)                             // game won
11613     {
11614       LevelSolved();
11615
11616       game_sp.game_over = TRUE;
11617
11618       game.all_players_gone = TRUE;
11619     }
11620
11621     if (game_sp.game_over)                              // game lost
11622       game.all_players_gone = TRUE;
11623   }
11624   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11625   {
11626     if (game_mm.level_solved &&
11627         !game_mm.game_over)                             // game won
11628     {
11629       LevelSolved();
11630
11631       game_mm.game_over = TRUE;
11632
11633       game.all_players_gone = TRUE;
11634     }
11635
11636     if (game_mm.game_over)                              // game lost
11637       game.all_players_gone = TRUE;
11638   }
11639 }
11640
11641 static void CheckLevelTime_StepCounter(void)
11642 {
11643   int i;
11644
11645   TimePlayed++;
11646
11647   if (TimeLeft > 0)
11648   {
11649     TimeLeft--;
11650
11651     if (TimeLeft <= 10 && game.time_limit && !game.LevelSolved)
11652       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11653
11654     game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11655
11656     DisplayGameControlValues();
11657
11658     if (!TimeLeft && game.time_limit && !game.LevelSolved)
11659       for (i = 0; i < MAX_PLAYERS; i++)
11660         KillPlayer(&stored_player[i]);
11661   }
11662   else if (game.no_level_time_limit && !game.all_players_gone)
11663   {
11664     game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11665
11666     DisplayGameControlValues();
11667   }
11668 }
11669
11670 static void CheckLevelTime(void)
11671 {
11672   int i;
11673
11674   if (TimeFrames >= FRAMES_PER_SECOND)
11675   {
11676     TimeFrames = 0;
11677     TapeTime++;
11678
11679     for (i = 0; i < MAX_PLAYERS; i++)
11680     {
11681       struct PlayerInfo *player = &stored_player[i];
11682
11683       if (SHIELD_ON(player))
11684       {
11685         player->shield_normal_time_left--;
11686
11687         if (player->shield_deadly_time_left > 0)
11688           player->shield_deadly_time_left--;
11689       }
11690     }
11691
11692     if (!game.LevelSolved && !level.use_step_counter)
11693     {
11694       TimePlayed++;
11695
11696       if (TimeLeft > 0)
11697       {
11698         TimeLeft--;
11699
11700         if (TimeLeft <= 10 && game.time_limit)
11701           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11702
11703         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11704            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11705
11706         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11707
11708         if (!TimeLeft && game.time_limit)
11709         {
11710           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11711             game_em.lev->killed_out_of_time = TRUE;
11712           else
11713             for (i = 0; i < MAX_PLAYERS; i++)
11714               KillPlayer(&stored_player[i]);
11715         }
11716       }
11717       else if (game.no_level_time_limit && !game.all_players_gone)
11718       {
11719         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11720       }
11721
11722       game_em.lev->time = (game.no_level_time_limit ? TimePlayed : TimeLeft);
11723     }
11724
11725     if (tape.recording || tape.playing)
11726       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11727   }
11728
11729   if (tape.recording || tape.playing)
11730     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11731
11732   UpdateAndDisplayGameControlValues();
11733 }
11734
11735 void AdvanceFrameAndPlayerCounters(int player_nr)
11736 {
11737   int i;
11738
11739   // advance frame counters (global frame counter and time frame counter)
11740   FrameCounter++;
11741   TimeFrames++;
11742
11743   // advance player counters (counters for move delay, move animation etc.)
11744   for (i = 0; i < MAX_PLAYERS; i++)
11745   {
11746     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11747     int move_delay_value = stored_player[i].move_delay_value;
11748     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11749
11750     if (!advance_player_counters)       // not all players may be affected
11751       continue;
11752
11753     if (move_frames == 0)       // less than one move per game frame
11754     {
11755       int stepsize = TILEX / move_delay_value;
11756       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11757       int count = (stored_player[i].is_moving ?
11758                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11759
11760       if (count % delay == 0)
11761         move_frames = 1;
11762     }
11763
11764     stored_player[i].Frame += move_frames;
11765
11766     if (stored_player[i].MovPos != 0)
11767       stored_player[i].StepFrame += move_frames;
11768
11769     if (stored_player[i].move_delay > 0)
11770       stored_player[i].move_delay--;
11771
11772     // due to bugs in previous versions, counter must count up, not down
11773     if (stored_player[i].push_delay != -1)
11774       stored_player[i].push_delay++;
11775
11776     if (stored_player[i].drop_delay > 0)
11777       stored_player[i].drop_delay--;
11778
11779     if (stored_player[i].is_dropping_pressed)
11780       stored_player[i].drop_pressed_delay++;
11781   }
11782 }
11783
11784 void AdvanceFrameCounter(void)
11785 {
11786   FrameCounter++;
11787 }
11788
11789 void AdvanceGfxFrame(void)
11790 {
11791   int x, y;
11792
11793   SCAN_PLAYFIELD(x, y)
11794   {
11795     GfxFrame[x][y]++;
11796   }
11797 }
11798
11799 static void HandleMouseAction(struct MouseActionInfo *mouse_action,
11800                               struct MouseActionInfo *mouse_action_last)
11801 {
11802   if (mouse_action->button)
11803   {
11804     int new_button = (mouse_action->button && mouse_action_last->button == 0);
11805     int ch_button = CH_SIDE_FROM_BUTTON(mouse_action->button);
11806     int x = mouse_action->lx;
11807     int y = mouse_action->ly;
11808     int element = Tile[x][y];
11809
11810     if (new_button)
11811     {
11812       CheckElementChangeByMouse(x, y, element, CE_CLICKED_BY_MOUSE, ch_button);
11813       CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_CLICKED_ON_X,
11814                                          ch_button);
11815     }
11816
11817     CheckElementChangeByMouse(x, y, element, CE_PRESSED_BY_MOUSE, ch_button);
11818     CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_PRESSED_ON_X,
11819                                        ch_button);
11820
11821     if (level.use_step_counter)
11822     {
11823       boolean counted_click = FALSE;
11824
11825       // element clicked that can change when clicked/pressed
11826       if (CAN_CHANGE_OR_HAS_ACTION(element) &&
11827           (HAS_ANY_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
11828            HAS_ANY_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE)))
11829         counted_click = TRUE;
11830
11831       // element clicked that can trigger change when clicked/pressed
11832       if (trigger_events[element][CE_MOUSE_CLICKED_ON_X] ||
11833           trigger_events[element][CE_MOUSE_PRESSED_ON_X])
11834         counted_click = TRUE;
11835
11836       if (new_button && counted_click)
11837         CheckLevelTime_StepCounter();
11838     }
11839   }
11840 }
11841
11842 void StartGameActions(boolean init_network_game, boolean record_tape,
11843                       int random_seed)
11844 {
11845   unsigned int new_random_seed = InitRND(random_seed);
11846
11847   if (record_tape)
11848     TapeStartRecording(new_random_seed);
11849
11850   if (setup.auto_pause_on_start && !tape.pausing)
11851     TapeTogglePause(TAPE_TOGGLE_MANUAL);
11852
11853   if (init_network_game)
11854   {
11855     SendToServer_LevelFile();
11856     SendToServer_StartPlaying();
11857
11858     return;
11859   }
11860
11861   InitGame();
11862 }
11863
11864 static void GameActionsExt(void)
11865 {
11866 #if 0
11867   static unsigned int game_frame_delay = 0;
11868 #endif
11869   unsigned int game_frame_delay_value;
11870   byte *recorded_player_action;
11871   byte summarized_player_action = 0;
11872   byte tape_action[MAX_TAPE_ACTIONS] = { 0 };
11873   int i;
11874
11875   // detect endless loops, caused by custom element programming
11876   if (recursion_loop_detected && recursion_loop_depth == 0)
11877   {
11878     char *message = getStringCat3("Internal Error! Element ",
11879                                   EL_NAME(recursion_loop_element),
11880                                   " caused endless loop! Quit the game?");
11881
11882     Warn("element '%s' caused endless loop in game engine",
11883          EL_NAME(recursion_loop_element));
11884
11885     RequestQuitGameExt(program.headless, level_editor_test_game, message);
11886
11887     recursion_loop_detected = FALSE;    // if game should be continued
11888
11889     free(message);
11890
11891     return;
11892   }
11893
11894   if (game.restart_level)
11895     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11896
11897   CheckLevelSolved();
11898
11899   if (game.LevelSolved && !game.LevelSolved_GameEnd)
11900     GameWon();
11901
11902   if (game.all_players_gone && !TAPE_IS_STOPPED(tape))
11903     TapeStop();
11904
11905   if (game_status != GAME_MODE_PLAYING)         // status might have changed
11906     return;
11907
11908   game_frame_delay_value =
11909     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11910
11911   if (tape.playing && tape.warp_forward && !tape.pausing)
11912     game_frame_delay_value = 0;
11913
11914   SetVideoFrameDelay(game_frame_delay_value);
11915
11916   // (de)activate virtual buttons depending on current game status
11917   if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
11918   {
11919     if (game.all_players_gone)  // if no players there to be controlled anymore
11920       SetOverlayActive(FALSE);
11921     else if (!tape.playing)     // if game continues after tape stopped playing
11922       SetOverlayActive(TRUE);
11923   }
11924
11925 #if 0
11926 #if 0
11927   // ---------- main game synchronization point ----------
11928
11929   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11930
11931   Debug("game:playing:skip", "skip == %d", skip);
11932
11933 #else
11934   // ---------- main game synchronization point ----------
11935
11936   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11937 #endif
11938 #endif
11939
11940   if (network_playing && !network_player_action_received)
11941   {
11942     // try to get network player actions in time
11943
11944     // last chance to get network player actions without main loop delay
11945     HandleNetworking();
11946
11947     // game was quit by network peer
11948     if (game_status != GAME_MODE_PLAYING)
11949       return;
11950
11951     // check if network player actions still missing and game still running
11952     if (!network_player_action_received && !checkGameEnded())
11953       return;           // failed to get network player actions in time
11954
11955     // do not yet reset "network_player_action_received" (for tape.pausing)
11956   }
11957
11958   if (tape.pausing)
11959     return;
11960
11961   // at this point we know that we really continue executing the game
11962
11963   network_player_action_received = FALSE;
11964
11965   // when playing tape, read previously recorded player input from tape data
11966   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11967
11968   local_player->effective_mouse_action = local_player->mouse_action;
11969
11970   if (recorded_player_action != NULL)
11971     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11972                                  recorded_player_action);
11973
11974   // TapePlayAction() may return NULL when toggling to "pause before death"
11975   if (tape.pausing)
11976     return;
11977
11978   if (tape.set_centered_player)
11979   {
11980     game.centered_player_nr_next = tape.centered_player_nr_next;
11981     game.set_centered_player = TRUE;
11982   }
11983
11984   for (i = 0; i < MAX_PLAYERS; i++)
11985   {
11986     summarized_player_action |= stored_player[i].action;
11987
11988     if (!network_playing && (game.team_mode || tape.playing))
11989       stored_player[i].effective_action = stored_player[i].action;
11990   }
11991
11992   if (network_playing && !checkGameEnded())
11993     SendToServer_MovePlayer(summarized_player_action);
11994
11995   // summarize all actions at local players mapped input device position
11996   // (this allows using different input devices in single player mode)
11997   if (!network.enabled && !game.team_mode)
11998     stored_player[map_player_action[local_player->index_nr]].effective_action =
11999       summarized_player_action;
12000
12001   // summarize all actions at centered player in local team mode
12002   if (tape.recording &&
12003       setup.team_mode && !network.enabled &&
12004       setup.input_on_focus &&
12005       game.centered_player_nr != -1)
12006   {
12007     for (i = 0; i < MAX_PLAYERS; i++)
12008       stored_player[map_player_action[i]].effective_action =
12009         (i == game.centered_player_nr ? summarized_player_action : 0);
12010   }
12011
12012   if (recorded_player_action != NULL)
12013     for (i = 0; i < MAX_PLAYERS; i++)
12014       stored_player[i].effective_action = recorded_player_action[i];
12015
12016   for (i = 0; i < MAX_PLAYERS; i++)
12017   {
12018     tape_action[i] = stored_player[i].effective_action;
12019
12020     /* (this may happen in the RND game engine if a player was not present on
12021        the playfield on level start, but appeared later from a custom element */
12022     if (setup.team_mode &&
12023         tape.recording &&
12024         tape_action[i] &&
12025         !tape.player_participates[i])
12026       tape.player_participates[i] = TRUE;
12027   }
12028
12029   SetTapeActionFromMouseAction(tape_action,
12030                                &local_player->effective_mouse_action);
12031
12032   // only record actions from input devices, but not programmed actions
12033   if (tape.recording)
12034     TapeRecordAction(tape_action);
12035
12036   // remember if game was played (especially after tape stopped playing)
12037   if (!tape.playing && summarized_player_action && !checkGameFailed())
12038     game.GamePlayed = TRUE;
12039
12040 #if USE_NEW_PLAYER_ASSIGNMENTS
12041   // !!! also map player actions in single player mode !!!
12042   // if (game.team_mode)
12043   if (1)
12044   {
12045     byte mapped_action[MAX_PLAYERS];
12046
12047 #if DEBUG_PLAYER_ACTIONS
12048     for (i = 0; i < MAX_PLAYERS; i++)
12049       DebugContinued("", "%d, ", stored_player[i].effective_action);
12050 #endif
12051
12052     for (i = 0; i < MAX_PLAYERS; i++)
12053       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
12054
12055     for (i = 0; i < MAX_PLAYERS; i++)
12056       stored_player[i].effective_action = mapped_action[i];
12057
12058 #if DEBUG_PLAYER_ACTIONS
12059     DebugContinued("", "=> ");
12060     for (i = 0; i < MAX_PLAYERS; i++)
12061       DebugContinued("", "%d, ", stored_player[i].effective_action);
12062     DebugContinued("game:playing:player", "\n");
12063 #endif
12064   }
12065 #if DEBUG_PLAYER_ACTIONS
12066   else
12067   {
12068     for (i = 0; i < MAX_PLAYERS; i++)
12069       DebugContinued("", "%d, ", stored_player[i].effective_action);
12070     DebugContinued("game:playing:player", "\n");
12071   }
12072 #endif
12073 #endif
12074
12075   for (i = 0; i < MAX_PLAYERS; i++)
12076   {
12077     // allow engine snapshot in case of changed movement attempt
12078     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
12079         (stored_player[i].effective_action & KEY_MOTION))
12080       game.snapshot.changed_action = TRUE;
12081
12082     // allow engine snapshot in case of snapping/dropping attempt
12083     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
12084         (stored_player[i].effective_action & KEY_BUTTON) != 0)
12085       game.snapshot.changed_action = TRUE;
12086
12087     game.snapshot.last_action[i] = stored_player[i].effective_action;
12088   }
12089
12090   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12091   {
12092     GameActions_EM_Main();
12093   }
12094   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12095   {
12096     GameActions_SP_Main();
12097   }
12098   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
12099   {
12100     GameActions_MM_Main();
12101   }
12102   else
12103   {
12104     GameActions_RND_Main();
12105   }
12106
12107   BlitScreenToBitmap(backbuffer);
12108
12109   CheckLevelSolved();
12110   CheckLevelTime();
12111
12112   AdvanceFrameAndPlayerCounters(-1);    // advance counters for all players
12113
12114   if (global.show_frames_per_second)
12115   {
12116     static unsigned int fps_counter = 0;
12117     static int fps_frames = 0;
12118     unsigned int fps_delay_ms = Counter() - fps_counter;
12119
12120     fps_frames++;
12121
12122     if (fps_delay_ms >= 500)    // calculate FPS every 0.5 seconds
12123     {
12124       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
12125
12126       fps_frames = 0;
12127       fps_counter = Counter();
12128
12129       // always draw FPS to screen after FPS value was updated
12130       redraw_mask |= REDRAW_FPS;
12131     }
12132
12133     // only draw FPS if no screen areas are deactivated (invisible warp mode)
12134     if (GetDrawDeactivationMask() == REDRAW_NONE)
12135       redraw_mask |= REDRAW_FPS;
12136   }
12137 }
12138
12139 static void GameActions_CheckSaveEngineSnapshot(void)
12140 {
12141   if (!game.snapshot.save_snapshot)
12142     return;
12143
12144   // clear flag for saving snapshot _before_ saving snapshot
12145   game.snapshot.save_snapshot = FALSE;
12146
12147   SaveEngineSnapshotToList();
12148 }
12149
12150 void GameActions(void)
12151 {
12152   GameActionsExt();
12153
12154   GameActions_CheckSaveEngineSnapshot();
12155 }
12156
12157 void GameActions_EM_Main(void)
12158 {
12159   byte effective_action[MAX_PLAYERS];
12160   int i;
12161
12162   for (i = 0; i < MAX_PLAYERS; i++)
12163     effective_action[i] = stored_player[i].effective_action;
12164
12165   GameActions_EM(effective_action);
12166 }
12167
12168 void GameActions_SP_Main(void)
12169 {
12170   byte effective_action[MAX_PLAYERS];
12171   int i;
12172
12173   for (i = 0; i < MAX_PLAYERS; i++)
12174     effective_action[i] = stored_player[i].effective_action;
12175
12176   GameActions_SP(effective_action);
12177
12178   for (i = 0; i < MAX_PLAYERS; i++)
12179   {
12180     if (stored_player[i].force_dropping)
12181       stored_player[i].action |= KEY_BUTTON_DROP;
12182
12183     stored_player[i].force_dropping = FALSE;
12184   }
12185 }
12186
12187 void GameActions_MM_Main(void)
12188 {
12189   AdvanceGfxFrame();
12190
12191   GameActions_MM(local_player->effective_mouse_action);
12192 }
12193
12194 void GameActions_RND_Main(void)
12195 {
12196   GameActions_RND();
12197 }
12198
12199 void GameActions_RND(void)
12200 {
12201   static struct MouseActionInfo mouse_action_last = { 0 };
12202   struct MouseActionInfo mouse_action = local_player->effective_mouse_action;
12203   int magic_wall_x = 0, magic_wall_y = 0;
12204   int i, x, y, element, graphic, last_gfx_frame;
12205
12206   InitPlayfieldScanModeVars();
12207
12208   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12209   {
12210     SCAN_PLAYFIELD(x, y)
12211     {
12212       ChangeCount[x][y] = 0;
12213       ChangeEvent[x][y] = -1;
12214     }
12215   }
12216
12217   if (game.set_centered_player)
12218   {
12219     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12220
12221     // switching to "all players" only possible if all players fit to screen
12222     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12223     {
12224       game.centered_player_nr_next = game.centered_player_nr;
12225       game.set_centered_player = FALSE;
12226     }
12227
12228     // do not switch focus to non-existing (or non-active) player
12229     if (game.centered_player_nr_next >= 0 &&
12230         !stored_player[game.centered_player_nr_next].active)
12231     {
12232       game.centered_player_nr_next = game.centered_player_nr;
12233       game.set_centered_player = FALSE;
12234     }
12235   }
12236
12237   if (game.set_centered_player &&
12238       ScreenMovPos == 0)        // screen currently aligned at tile position
12239   {
12240     int sx, sy;
12241
12242     if (game.centered_player_nr_next == -1)
12243     {
12244       setScreenCenteredToAllPlayers(&sx, &sy);
12245     }
12246     else
12247     {
12248       sx = stored_player[game.centered_player_nr_next].jx;
12249       sy = stored_player[game.centered_player_nr_next].jy;
12250     }
12251
12252     game.centered_player_nr = game.centered_player_nr_next;
12253     game.set_centered_player = FALSE;
12254
12255     DrawRelocateScreen(0, 0, sx, sy, TRUE, setup.quick_switch);
12256     DrawGameDoorValues();
12257   }
12258
12259   // check single step mode (set flag and clear again if any player is active)
12260   game.enter_single_step_mode =
12261     (tape.single_step && tape.recording && !tape.pausing);
12262
12263   for (i = 0; i < MAX_PLAYERS; i++)
12264   {
12265     int actual_player_action = stored_player[i].effective_action;
12266
12267 #if 1
12268     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12269        - rnd_equinox_tetrachloride 048
12270        - rnd_equinox_tetrachloride_ii 096
12271        - rnd_emanuel_schmieg 002
12272        - doctor_sloan_ww 001, 020
12273     */
12274     if (stored_player[i].MovPos == 0)
12275       CheckGravityMovement(&stored_player[i]);
12276 #endif
12277
12278     // overwrite programmed action with tape action
12279     if (stored_player[i].programmed_action)
12280       actual_player_action = stored_player[i].programmed_action;
12281
12282     PlayerActions(&stored_player[i], actual_player_action);
12283
12284     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12285   }
12286
12287   // single step pause mode may already have been toggled by "ScrollPlayer()"
12288   if (game.enter_single_step_mode && !tape.pausing)
12289     TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12290
12291   ScrollScreen(NULL, SCROLL_GO_ON);
12292
12293   /* for backwards compatibility, the following code emulates a fixed bug that
12294      occured when pushing elements (causing elements that just made their last
12295      pushing step to already (if possible) make their first falling step in the
12296      same game frame, which is bad); this code is also needed to use the famous
12297      "spring push bug" which is used in older levels and might be wanted to be
12298      used also in newer levels, but in this case the buggy pushing code is only
12299      affecting the "spring" element and no other elements */
12300
12301   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12302   {
12303     for (i = 0; i < MAX_PLAYERS; i++)
12304     {
12305       struct PlayerInfo *player = &stored_player[i];
12306       int x = player->jx;
12307       int y = player->jy;
12308
12309       if (player->active && player->is_pushing && player->is_moving &&
12310           IS_MOVING(x, y) &&
12311           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12312            Tile[x][y] == EL_SPRING))
12313       {
12314         ContinueMoving(x, y);
12315
12316         // continue moving after pushing (this is actually a bug)
12317         if (!IS_MOVING(x, y))
12318           Stop[x][y] = FALSE;
12319       }
12320     }
12321   }
12322
12323   SCAN_PLAYFIELD(x, y)
12324   {
12325     Last[x][y] = Tile[x][y];
12326
12327     ChangeCount[x][y] = 0;
12328     ChangeEvent[x][y] = -1;
12329
12330     // this must be handled before main playfield loop
12331     if (Tile[x][y] == EL_PLAYER_IS_LEAVING)
12332     {
12333       MovDelay[x][y]--;
12334       if (MovDelay[x][y] <= 0)
12335         RemoveField(x, y);
12336     }
12337
12338     if (Tile[x][y] == EL_ELEMENT_SNAPPING)
12339     {
12340       MovDelay[x][y]--;
12341       if (MovDelay[x][y] <= 0)
12342       {
12343         int element = Store[x][y];
12344         int move_direction = MovDir[x][y];
12345         int player_index_bit = Store2[x][y];
12346
12347         Store[x][y] = 0;
12348         Store2[x][y] = 0;
12349
12350         RemoveField(x, y);
12351         TEST_DrawLevelField(x, y);
12352
12353         TestFieldAfterSnapping(x, y, element, move_direction, player_index_bit);
12354
12355         if (IS_ENVELOPE(element))
12356           local_player->show_envelope = element;
12357       }
12358     }
12359
12360 #if DEBUG
12361     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12362     {
12363       Debug("game:playing:GameActions_RND", "x = %d, y = %d: ChangePage != -1",
12364             x, y);
12365       Debug("game:playing:GameActions_RND", "This should never happen!");
12366
12367       ChangePage[x][y] = -1;
12368     }
12369 #endif
12370
12371     Stop[x][y] = FALSE;
12372     if (WasJustMoving[x][y] > 0)
12373       WasJustMoving[x][y]--;
12374     if (WasJustFalling[x][y] > 0)
12375       WasJustFalling[x][y]--;
12376     if (CheckCollision[x][y] > 0)
12377       CheckCollision[x][y]--;
12378     if (CheckImpact[x][y] > 0)
12379       CheckImpact[x][y]--;
12380
12381     GfxFrame[x][y]++;
12382
12383     /* reset finished pushing action (not done in ContinueMoving() to allow
12384        continuous pushing animation for elements with zero push delay) */
12385     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12386     {
12387       ResetGfxAnimation(x, y);
12388       TEST_DrawLevelField(x, y);
12389     }
12390
12391 #if DEBUG
12392     if (IS_BLOCKED(x, y))
12393     {
12394       int oldx, oldy;
12395
12396       Blocked2Moving(x, y, &oldx, &oldy);
12397       if (!IS_MOVING(oldx, oldy))
12398       {
12399         Debug("game:playing:GameActions_RND", "(BLOCKED => MOVING) context corrupted!");
12400         Debug("game:playing:GameActions_RND", "BLOCKED: x = %d, y = %d", x, y);
12401         Debug("game:playing:GameActions_RND", "!MOVING: oldx = %d, oldy = %d", oldx, oldy);
12402         Debug("game:playing:GameActions_RND", "This should never happen!");
12403       }
12404     }
12405 #endif
12406   }
12407
12408   HandleMouseAction(&mouse_action, &mouse_action_last);
12409
12410   SCAN_PLAYFIELD(x, y)
12411   {
12412     element = Tile[x][y];
12413     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12414     last_gfx_frame = GfxFrame[x][y];
12415
12416     if (element == EL_EMPTY)
12417       graphic = el2img(GfxElementEmpty[x][y]);
12418
12419     ResetGfxFrame(x, y);
12420
12421     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
12422       DrawLevelGraphicAnimation(x, y, graphic);
12423
12424     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12425         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12426       ResetRandomAnimationValue(x, y);
12427
12428     SetRandomAnimationValue(x, y);
12429
12430     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12431
12432     if (IS_INACTIVE(element))
12433     {
12434       if (IS_ANIMATED(graphic))
12435         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12436
12437       continue;
12438     }
12439
12440     // this may take place after moving, so 'element' may have changed
12441     if (IS_CHANGING(x, y) &&
12442         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12443     {
12444       int page = element_info[element].event_page_nr[CE_DELAY];
12445
12446       HandleElementChange(x, y, page);
12447
12448       element = Tile[x][y];
12449       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12450     }
12451
12452     CheckNextToConditions(x, y);
12453
12454     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12455     {
12456       StartMoving(x, y);
12457
12458       element = Tile[x][y];
12459       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12460
12461       if (IS_ANIMATED(graphic) &&
12462           !IS_MOVING(x, y) &&
12463           !Stop[x][y])
12464         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12465
12466       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12467         TEST_DrawTwinkleOnField(x, y);
12468     }
12469     else if (element == EL_ACID)
12470     {
12471       if (!Stop[x][y])
12472         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12473     }
12474     else if ((element == EL_EXIT_OPEN ||
12475               element == EL_EM_EXIT_OPEN ||
12476               element == EL_SP_EXIT_OPEN ||
12477               element == EL_STEEL_EXIT_OPEN ||
12478               element == EL_EM_STEEL_EXIT_OPEN ||
12479               element == EL_SP_TERMINAL ||
12480               element == EL_SP_TERMINAL_ACTIVE ||
12481               element == EL_EXTRA_TIME ||
12482               element == EL_SHIELD_NORMAL ||
12483               element == EL_SHIELD_DEADLY) &&
12484              IS_ANIMATED(graphic))
12485       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12486     else if (IS_MOVING(x, y))
12487       ContinueMoving(x, y);
12488     else if (IS_ACTIVE_BOMB(element))
12489       CheckDynamite(x, y);
12490     else if (element == EL_AMOEBA_GROWING)
12491       AmoebaGrowing(x, y);
12492     else if (element == EL_AMOEBA_SHRINKING)
12493       AmoebaShrinking(x, y);
12494
12495 #if !USE_NEW_AMOEBA_CODE
12496     else if (IS_AMOEBALIVE(element))
12497       AmoebaReproduce(x, y);
12498 #endif
12499
12500     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12501       Life(x, y);
12502     else if (element == EL_EXIT_CLOSED)
12503       CheckExit(x, y);
12504     else if (element == EL_EM_EXIT_CLOSED)
12505       CheckExitEM(x, y);
12506     else if (element == EL_STEEL_EXIT_CLOSED)
12507       CheckExitSteel(x, y);
12508     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12509       CheckExitSteelEM(x, y);
12510     else if (element == EL_SP_EXIT_CLOSED)
12511       CheckExitSP(x, y);
12512     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12513              element == EL_EXPANDABLE_STEELWALL_GROWING)
12514       WallGrowing(x, y);
12515     else if (element == EL_EXPANDABLE_WALL ||
12516              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12517              element == EL_EXPANDABLE_WALL_VERTICAL ||
12518              element == EL_EXPANDABLE_WALL_ANY ||
12519              element == EL_BD_EXPANDABLE_WALL ||
12520              element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12521              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12522              element == EL_EXPANDABLE_STEELWALL_ANY)
12523       CheckWallGrowing(x, y);
12524     else if (element == EL_FLAMES)
12525       CheckForDragon(x, y);
12526     else if (element == EL_EXPLOSION)
12527       ; // drawing of correct explosion animation is handled separately
12528     else if (element == EL_ELEMENT_SNAPPING ||
12529              element == EL_DIAGONAL_SHRINKING ||
12530              element == EL_DIAGONAL_GROWING)
12531     {
12532       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y], GfxDir[x][y]);
12533
12534       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12535     }
12536     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12537       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12538
12539     if (IS_BELT_ACTIVE(element))
12540       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12541
12542     if (game.magic_wall_active)
12543     {
12544       int jx = local_player->jx, jy = local_player->jy;
12545
12546       // play the element sound at the position nearest to the player
12547       if ((element == EL_MAGIC_WALL_FULL ||
12548            element == EL_MAGIC_WALL_ACTIVE ||
12549            element == EL_MAGIC_WALL_EMPTYING ||
12550            element == EL_BD_MAGIC_WALL_FULL ||
12551            element == EL_BD_MAGIC_WALL_ACTIVE ||
12552            element == EL_BD_MAGIC_WALL_EMPTYING ||
12553            element == EL_DC_MAGIC_WALL_FULL ||
12554            element == EL_DC_MAGIC_WALL_ACTIVE ||
12555            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12556           ABS(x - jx) + ABS(y - jy) <
12557           ABS(magic_wall_x - jx) + ABS(magic_wall_y - jy))
12558       {
12559         magic_wall_x = x;
12560         magic_wall_y = y;
12561       }
12562     }
12563   }
12564
12565 #if USE_NEW_AMOEBA_CODE
12566   // new experimental amoeba growth stuff
12567   if (!(FrameCounter % 8))
12568   {
12569     static unsigned int random = 1684108901;
12570
12571     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12572     {
12573       x = RND(lev_fieldx);
12574       y = RND(lev_fieldy);
12575       element = Tile[x][y];
12576
12577       if (!IS_PLAYER(x, y) &&
12578           (element == EL_EMPTY ||
12579            CAN_GROW_INTO(element) ||
12580            element == EL_QUICKSAND_EMPTY ||
12581            element == EL_QUICKSAND_FAST_EMPTY ||
12582            element == EL_ACID_SPLASH_LEFT ||
12583            element == EL_ACID_SPLASH_RIGHT))
12584       {
12585         if ((IN_LEV_FIELD(x, y - 1) && Tile[x][y - 1] == EL_AMOEBA_WET) ||
12586             (IN_LEV_FIELD(x - 1, y) && Tile[x - 1][y] == EL_AMOEBA_WET) ||
12587             (IN_LEV_FIELD(x + 1, y) && Tile[x + 1][y] == EL_AMOEBA_WET) ||
12588             (IN_LEV_FIELD(x, y + 1) && Tile[x][y + 1] == EL_AMOEBA_WET))
12589           Tile[x][y] = EL_AMOEBA_DROP;
12590       }
12591
12592       random = random * 129 + 1;
12593     }
12594   }
12595 #endif
12596
12597   game.explosions_delayed = FALSE;
12598
12599   SCAN_PLAYFIELD(x, y)
12600   {
12601     element = Tile[x][y];
12602
12603     if (ExplodeField[x][y])
12604       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12605     else if (element == EL_EXPLOSION)
12606       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12607
12608     ExplodeField[x][y] = EX_TYPE_NONE;
12609   }
12610
12611   game.explosions_delayed = TRUE;
12612
12613   if (game.magic_wall_active)
12614   {
12615     if (!(game.magic_wall_time_left % 4))
12616     {
12617       int element = Tile[magic_wall_x][magic_wall_y];
12618
12619       if (element == EL_BD_MAGIC_WALL_FULL ||
12620           element == EL_BD_MAGIC_WALL_ACTIVE ||
12621           element == EL_BD_MAGIC_WALL_EMPTYING)
12622         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12623       else if (element == EL_DC_MAGIC_WALL_FULL ||
12624                element == EL_DC_MAGIC_WALL_ACTIVE ||
12625                element == EL_DC_MAGIC_WALL_EMPTYING)
12626         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12627       else
12628         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12629     }
12630
12631     if (game.magic_wall_time_left > 0)
12632     {
12633       game.magic_wall_time_left--;
12634
12635       if (!game.magic_wall_time_left)
12636       {
12637         SCAN_PLAYFIELD(x, y)
12638         {
12639           element = Tile[x][y];
12640
12641           if (element == EL_MAGIC_WALL_ACTIVE ||
12642               element == EL_MAGIC_WALL_FULL)
12643           {
12644             Tile[x][y] = EL_MAGIC_WALL_DEAD;
12645             TEST_DrawLevelField(x, y);
12646           }
12647           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12648                    element == EL_BD_MAGIC_WALL_FULL)
12649           {
12650             Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
12651             TEST_DrawLevelField(x, y);
12652           }
12653           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12654                    element == EL_DC_MAGIC_WALL_FULL)
12655           {
12656             Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
12657             TEST_DrawLevelField(x, y);
12658           }
12659         }
12660
12661         game.magic_wall_active = FALSE;
12662       }
12663     }
12664   }
12665
12666   if (game.light_time_left > 0)
12667   {
12668     game.light_time_left--;
12669
12670     if (game.light_time_left == 0)
12671       RedrawAllLightSwitchesAndInvisibleElements();
12672   }
12673
12674   if (game.timegate_time_left > 0)
12675   {
12676     game.timegate_time_left--;
12677
12678     if (game.timegate_time_left == 0)
12679       CloseAllOpenTimegates();
12680   }
12681
12682   if (game.lenses_time_left > 0)
12683   {
12684     game.lenses_time_left--;
12685
12686     if (game.lenses_time_left == 0)
12687       RedrawAllInvisibleElementsForLenses();
12688   }
12689
12690   if (game.magnify_time_left > 0)
12691   {
12692     game.magnify_time_left--;
12693
12694     if (game.magnify_time_left == 0)
12695       RedrawAllInvisibleElementsForMagnifier();
12696   }
12697
12698   for (i = 0; i < MAX_PLAYERS; i++)
12699   {
12700     struct PlayerInfo *player = &stored_player[i];
12701
12702     if (SHIELD_ON(player))
12703     {
12704       if (player->shield_deadly_time_left)
12705         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12706       else if (player->shield_normal_time_left)
12707         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12708     }
12709   }
12710
12711 #if USE_DELAYED_GFX_REDRAW
12712   SCAN_PLAYFIELD(x, y)
12713   {
12714     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12715     {
12716       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12717          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12718
12719       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12720         DrawLevelField(x, y);
12721
12722       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12723         DrawLevelFieldCrumbled(x, y);
12724
12725       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12726         DrawLevelFieldCrumbledNeighbours(x, y);
12727
12728       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12729         DrawTwinkleOnField(x, y);
12730     }
12731
12732     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12733   }
12734 #endif
12735
12736   DrawAllPlayers();
12737   PlayAllPlayersSound();
12738
12739   for (i = 0; i < MAX_PLAYERS; i++)
12740   {
12741     struct PlayerInfo *player = &stored_player[i];
12742
12743     if (player->show_envelope != 0 && (!player->active ||
12744                                        player->MovPos == 0))
12745     {
12746       ShowEnvelope(player->show_envelope - EL_ENVELOPE_1);
12747
12748       player->show_envelope = 0;
12749     }
12750   }
12751
12752   // use random number generator in every frame to make it less predictable
12753   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12754     RND(1);
12755
12756   mouse_action_last = mouse_action;
12757 }
12758
12759 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12760 {
12761   int min_x = x, min_y = y, max_x = x, max_y = y;
12762   int scr_fieldx = getScreenFieldSizeX();
12763   int scr_fieldy = getScreenFieldSizeY();
12764   int i;
12765
12766   for (i = 0; i < MAX_PLAYERS; i++)
12767   {
12768     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12769
12770     if (!stored_player[i].active || &stored_player[i] == player)
12771       continue;
12772
12773     min_x = MIN(min_x, jx);
12774     min_y = MIN(min_y, jy);
12775     max_x = MAX(max_x, jx);
12776     max_y = MAX(max_y, jy);
12777   }
12778
12779   return (max_x - min_x < scr_fieldx && max_y - min_y < scr_fieldy);
12780 }
12781
12782 static boolean AllPlayersInVisibleScreen(void)
12783 {
12784   int i;
12785
12786   for (i = 0; i < MAX_PLAYERS; i++)
12787   {
12788     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12789
12790     if (!stored_player[i].active)
12791       continue;
12792
12793     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12794       return FALSE;
12795   }
12796
12797   return TRUE;
12798 }
12799
12800 void ScrollLevel(int dx, int dy)
12801 {
12802   int scroll_offset = 2 * TILEX_VAR;
12803   int x, y;
12804
12805   BlitBitmap(drawto_field, drawto_field,
12806              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12807              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12808              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12809              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12810              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12811              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12812
12813   if (dx != 0)
12814   {
12815     x = (dx == 1 ? BX1 : BX2);
12816     for (y = BY1; y <= BY2; y++)
12817       DrawScreenField(x, y);
12818   }
12819
12820   if (dy != 0)
12821   {
12822     y = (dy == 1 ? BY1 : BY2);
12823     for (x = BX1; x <= BX2; x++)
12824       DrawScreenField(x, y);
12825   }
12826
12827   redraw_mask |= REDRAW_FIELD;
12828 }
12829
12830 static boolean canFallDown(struct PlayerInfo *player)
12831 {
12832   int jx = player->jx, jy = player->jy;
12833
12834   return (IN_LEV_FIELD(jx, jy + 1) &&
12835           (IS_FREE(jx, jy + 1) ||
12836            (Tile[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12837           IS_WALKABLE_FROM(Tile[jx][jy], MV_DOWN) &&
12838           !IS_WALKABLE_INSIDE(Tile[jx][jy]));
12839 }
12840
12841 static boolean canPassField(int x, int y, int move_dir)
12842 {
12843   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12844   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12845   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12846   int nextx = x + dx;
12847   int nexty = y + dy;
12848   int element = Tile[x][y];
12849
12850   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12851           !CAN_MOVE(element) &&
12852           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12853           IS_WALKABLE_FROM(Tile[nextx][nexty], move_dir) &&
12854           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12855 }
12856
12857 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12858 {
12859   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12860   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12861   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12862   int newx = x + dx;
12863   int newy = y + dy;
12864
12865   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12866           IS_GRAVITY_REACHABLE(Tile[newx][newy]) &&
12867           (IS_DIGGABLE(Tile[newx][newy]) ||
12868            IS_WALKABLE_FROM(Tile[newx][newy], opposite_dir) ||
12869            canPassField(newx, newy, move_dir)));
12870 }
12871
12872 static void CheckGravityMovement(struct PlayerInfo *player)
12873 {
12874   if (player->gravity && !player->programmed_action)
12875   {
12876     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12877     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12878     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12879     int jx = player->jx, jy = player->jy;
12880     boolean player_is_moving_to_valid_field =
12881       (!player_is_snapping &&
12882        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12883         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12884     boolean player_can_fall_down = canFallDown(player);
12885
12886     if (player_can_fall_down &&
12887         !player_is_moving_to_valid_field)
12888       player->programmed_action = MV_DOWN;
12889   }
12890 }
12891
12892 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12893 {
12894   return CheckGravityMovement(player);
12895
12896   if (player->gravity && !player->programmed_action)
12897   {
12898     int jx = player->jx, jy = player->jy;
12899     boolean field_under_player_is_free =
12900       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12901     boolean player_is_standing_on_valid_field =
12902       (IS_WALKABLE_INSIDE(Tile[jx][jy]) ||
12903        (IS_WALKABLE(Tile[jx][jy]) &&
12904         !(element_info[Tile[jx][jy]].access_direction & MV_DOWN)));
12905
12906     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12907       player->programmed_action = MV_DOWN;
12908   }
12909 }
12910
12911 /*
12912   MovePlayerOneStep()
12913   -----------------------------------------------------------------------------
12914   dx, dy:               direction (non-diagonal) to try to move the player to
12915   real_dx, real_dy:     direction as read from input device (can be diagonal)
12916 */
12917
12918 boolean MovePlayerOneStep(struct PlayerInfo *player,
12919                           int dx, int dy, int real_dx, int real_dy)
12920 {
12921   int jx = player->jx, jy = player->jy;
12922   int new_jx = jx + dx, new_jy = jy + dy;
12923   int can_move;
12924   boolean player_can_move = !player->cannot_move;
12925
12926   if (!player->active || (!dx && !dy))
12927     return MP_NO_ACTION;
12928
12929   player->MovDir = (dx < 0 ? MV_LEFT :
12930                     dx > 0 ? MV_RIGHT :
12931                     dy < 0 ? MV_UP :
12932                     dy > 0 ? MV_DOWN :  MV_NONE);
12933
12934   if (!IN_LEV_FIELD(new_jx, new_jy))
12935     return MP_NO_ACTION;
12936
12937   if (!player_can_move)
12938   {
12939     if (player->MovPos == 0)
12940     {
12941       player->is_moving = FALSE;
12942       player->is_digging = FALSE;
12943       player->is_collecting = FALSE;
12944       player->is_snapping = FALSE;
12945       player->is_pushing = FALSE;
12946     }
12947   }
12948
12949   if (!network.enabled && game.centered_player_nr == -1 &&
12950       !AllPlayersInSight(player, new_jx, new_jy))
12951     return MP_NO_ACTION;
12952
12953   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx, real_dy, DF_DIG);
12954   if (can_move != MP_MOVING)
12955     return can_move;
12956
12957   // check if DigField() has caused relocation of the player
12958   if (player->jx != jx || player->jy != jy)
12959     return MP_NO_ACTION;        // <-- !!! CHECK THIS [-> MP_ACTION ?] !!!
12960
12961   StorePlayer[jx][jy] = 0;
12962   player->last_jx = jx;
12963   player->last_jy = jy;
12964   player->jx = new_jx;
12965   player->jy = new_jy;
12966   StorePlayer[new_jx][new_jy] = player->element_nr;
12967
12968   if (player->move_delay_value_next != -1)
12969   {
12970     player->move_delay_value = player->move_delay_value_next;
12971     player->move_delay_value_next = -1;
12972   }
12973
12974   player->MovPos =
12975     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12976
12977   player->step_counter++;
12978
12979   PlayerVisit[jx][jy] = FrameCounter;
12980
12981   player->is_moving = TRUE;
12982
12983 #if 1
12984   // should better be called in MovePlayer(), but this breaks some tapes
12985   ScrollPlayer(player, SCROLL_INIT);
12986 #endif
12987
12988   return MP_MOVING;
12989 }
12990
12991 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12992 {
12993   int jx = player->jx, jy = player->jy;
12994   int old_jx = jx, old_jy = jy;
12995   int moved = MP_NO_ACTION;
12996
12997   if (!player->active)
12998     return FALSE;
12999
13000   if (!dx && !dy)
13001   {
13002     if (player->MovPos == 0)
13003     {
13004       player->is_moving = FALSE;
13005       player->is_digging = FALSE;
13006       player->is_collecting = FALSE;
13007       player->is_snapping = FALSE;
13008       player->is_pushing = FALSE;
13009     }
13010
13011     return FALSE;
13012   }
13013
13014   if (player->move_delay > 0)
13015     return FALSE;
13016
13017   player->move_delay = -1;              // set to "uninitialized" value
13018
13019   // store if player is automatically moved to next field
13020   player->is_auto_moving = (player->programmed_action != MV_NONE);
13021
13022   // remove the last programmed player action
13023   player->programmed_action = 0;
13024
13025   if (player->MovPos)
13026   {
13027     // should only happen if pre-1.2 tape recordings are played
13028     // this is only for backward compatibility
13029
13030     int original_move_delay_value = player->move_delay_value;
13031
13032 #if DEBUG
13033     Debug("game:playing:MovePlayer",
13034           "THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]",
13035           tape.counter);
13036 #endif
13037
13038     // scroll remaining steps with finest movement resolution
13039     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
13040
13041     while (player->MovPos)
13042     {
13043       ScrollPlayer(player, SCROLL_GO_ON);
13044       ScrollScreen(NULL, SCROLL_GO_ON);
13045
13046       AdvanceFrameAndPlayerCounters(player->index_nr);
13047
13048       DrawAllPlayers();
13049       BackToFront_WithFrameDelay(0);
13050     }
13051
13052     player->move_delay_value = original_move_delay_value;
13053   }
13054
13055   player->is_active = FALSE;
13056
13057   if (player->last_move_dir & MV_HORIZONTAL)
13058   {
13059     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
13060       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
13061   }
13062   else
13063   {
13064     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
13065       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
13066   }
13067
13068   if (!moved && !player->is_active)
13069   {
13070     player->is_moving = FALSE;
13071     player->is_digging = FALSE;
13072     player->is_collecting = FALSE;
13073     player->is_snapping = FALSE;
13074     player->is_pushing = FALSE;
13075   }
13076
13077   jx = player->jx;
13078   jy = player->jy;
13079
13080   if (moved & MP_MOVING && !ScreenMovPos &&
13081       (player->index_nr == game.centered_player_nr ||
13082        game.centered_player_nr == -1))
13083   {
13084     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
13085
13086     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13087     {
13088       // actual player has left the screen -- scroll in that direction
13089       if (jx != old_jx)         // player has moved horizontally
13090         scroll_x += (jx - old_jx);
13091       else                      // player has moved vertically
13092         scroll_y += (jy - old_jy);
13093     }
13094     else
13095     {
13096       int offset_raw = game.scroll_delay_value;
13097
13098       if (jx != old_jx)         // player has moved horizontally
13099       {
13100         int offset = MIN(offset_raw, (SCR_FIELDX - 2) / 2);
13101         int offset_x = offset * (player->MovDir == MV_LEFT ? +1 : -1);
13102         int new_scroll_x = jx - MIDPOSX + offset_x;
13103
13104         if ((player->MovDir == MV_LEFT  && scroll_x > new_scroll_x) ||
13105             (player->MovDir == MV_RIGHT && scroll_x < new_scroll_x))
13106           scroll_x = new_scroll_x;
13107
13108         // don't scroll over playfield boundaries
13109         scroll_x = MIN(MAX(SBX_Left, scroll_x), SBX_Right);
13110
13111         // don't scroll more than one field at a time
13112         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13113
13114         // don't scroll against the player's moving direction
13115         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
13116             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13117           scroll_x = old_scroll_x;
13118       }
13119       else                      // player has moved vertically
13120       {
13121         int offset = MIN(offset_raw, (SCR_FIELDY - 2) / 2);
13122         int offset_y = offset * (player->MovDir == MV_UP ? +1 : -1);
13123         int new_scroll_y = jy - MIDPOSY + offset_y;
13124
13125         if ((player->MovDir == MV_UP   && scroll_y > new_scroll_y) ||
13126             (player->MovDir == MV_DOWN && scroll_y < new_scroll_y))
13127           scroll_y = new_scroll_y;
13128
13129         // don't scroll over playfield boundaries
13130         scroll_y = MIN(MAX(SBY_Upper, scroll_y), SBY_Lower);
13131
13132         // don't scroll more than one field at a time
13133         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13134
13135         // don't scroll against the player's moving direction
13136         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
13137             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13138           scroll_y = old_scroll_y;
13139       }
13140     }
13141
13142     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13143     {
13144       if (!network.enabled && game.centered_player_nr == -1 &&
13145           !AllPlayersInVisibleScreen())
13146       {
13147         scroll_x = old_scroll_x;
13148         scroll_y = old_scroll_y;
13149       }
13150       else
13151       {
13152         ScrollScreen(player, SCROLL_INIT);
13153         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13154       }
13155     }
13156   }
13157
13158   player->StepFrame = 0;
13159
13160   if (moved & MP_MOVING)
13161   {
13162     if (old_jx != jx && old_jy == jy)
13163       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13164     else if (old_jx == jx && old_jy != jy)
13165       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13166
13167     TEST_DrawLevelField(jx, jy);        // for "crumbled sand"
13168
13169     player->last_move_dir = player->MovDir;
13170     player->is_moving = TRUE;
13171     player->is_snapping = FALSE;
13172     player->is_switching = FALSE;
13173     player->is_dropping = FALSE;
13174     player->is_dropping_pressed = FALSE;
13175     player->drop_pressed_delay = 0;
13176
13177 #if 0
13178     // should better be called here than above, but this breaks some tapes
13179     ScrollPlayer(player, SCROLL_INIT);
13180 #endif
13181   }
13182   else
13183   {
13184     CheckGravityMovementWhenNotMoving(player);
13185
13186     player->is_moving = FALSE;
13187
13188     /* at this point, the player is allowed to move, but cannot move right now
13189        (e.g. because of something blocking the way) -- ensure that the player
13190        is also allowed to move in the next frame (in old versions before 3.1.1,
13191        the player was forced to wait again for eight frames before next try) */
13192
13193     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13194       player->move_delay = 0;   // allow direct movement in the next frame
13195   }
13196
13197   if (player->move_delay == -1)         // not yet initialized by DigField()
13198     player->move_delay = player->move_delay_value;
13199
13200   if (game.engine_version < VERSION_IDENT(3,0,7,0))
13201   {
13202     TestIfPlayerTouchesBadThing(jx, jy);
13203     TestIfPlayerTouchesCustomElement(jx, jy);
13204   }
13205
13206   if (!player->active)
13207     RemovePlayer(player);
13208
13209   return moved;
13210 }
13211
13212 void ScrollPlayer(struct PlayerInfo *player, int mode)
13213 {
13214   int jx = player->jx, jy = player->jy;
13215   int last_jx = player->last_jx, last_jy = player->last_jy;
13216   int move_stepsize = TILEX / player->move_delay_value;
13217
13218   if (!player->active)
13219     return;
13220
13221   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      // player not moving
13222     return;
13223
13224   if (mode == SCROLL_INIT)
13225   {
13226     player->actual_frame_counter.count = FrameCounter;
13227     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13228
13229     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13230         Tile[last_jx][last_jy] == EL_EMPTY)
13231     {
13232       int last_field_block_delay = 0;   // start with no blocking at all
13233       int block_delay_adjustment = player->block_delay_adjustment;
13234
13235       // if player blocks last field, add delay for exactly one move
13236       if (player->block_last_field)
13237       {
13238         last_field_block_delay += player->move_delay_value;
13239
13240         // when blocking enabled, prevent moving up despite gravity
13241         if (player->gravity && player->MovDir == MV_UP)
13242           block_delay_adjustment = -1;
13243       }
13244
13245       // add block delay adjustment (also possible when not blocking)
13246       last_field_block_delay += block_delay_adjustment;
13247
13248       Tile[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13249       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13250     }
13251
13252     if (player->MovPos != 0)    // player has not yet reached destination
13253       return;
13254   }
13255   else if (!FrameReached(&player->actual_frame_counter))
13256     return;
13257
13258   if (player->MovPos != 0)
13259   {
13260     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13261     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13262
13263     // before DrawPlayer() to draw correct player graphic for this case
13264     if (player->MovPos == 0)
13265       CheckGravityMovement(player);
13266   }
13267
13268   if (player->MovPos == 0)      // player reached destination field
13269   {
13270     if (player->move_delay_reset_counter > 0)
13271     {
13272       player->move_delay_reset_counter--;
13273
13274       if (player->move_delay_reset_counter == 0)
13275       {
13276         // continue with normal speed after quickly moving through gate
13277         HALVE_PLAYER_SPEED(player);
13278
13279         // be able to make the next move without delay
13280         player->move_delay = 0;
13281       }
13282     }
13283
13284     if (Tile[jx][jy] == EL_EXIT_OPEN ||
13285         Tile[jx][jy] == EL_EM_EXIT_OPEN ||
13286         Tile[jx][jy] == EL_EM_EXIT_OPENING ||
13287         Tile[jx][jy] == EL_STEEL_EXIT_OPEN ||
13288         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13289         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
13290         Tile[jx][jy] == EL_SP_EXIT_OPEN ||
13291         Tile[jx][jy] == EL_SP_EXIT_OPENING)     // <-- special case
13292     {
13293       ExitPlayer(player);
13294
13295       if (game.players_still_needed == 0 &&
13296           (game.friends_still_needed == 0 ||
13297            IS_SP_ELEMENT(Tile[jx][jy])))
13298         LevelSolved();
13299     }
13300
13301     player->last_jx = jx;
13302     player->last_jy = jy;
13303
13304     // this breaks one level: "machine", level 000
13305     {
13306       int move_direction = player->MovDir;
13307       int enter_side = MV_DIR_OPPOSITE(move_direction);
13308       int leave_side = move_direction;
13309       int old_jx = last_jx;
13310       int old_jy = last_jy;
13311       int old_element = Tile[old_jx][old_jy];
13312       int new_element = Tile[jx][jy];
13313
13314       if (IS_CUSTOM_ELEMENT(old_element))
13315         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13316                                    CE_LEFT_BY_PLAYER,
13317                                    player->index_bit, leave_side);
13318
13319       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13320                                           CE_PLAYER_LEAVES_X,
13321                                           player->index_bit, leave_side);
13322
13323       // needed because pushed element has not yet reached its destination,
13324       // so it would trigger a change event at its previous field location
13325       if (!player->is_pushing)
13326       {
13327         if (IS_CUSTOM_ELEMENT(new_element))
13328           CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13329                                      player->index_bit, enter_side);
13330
13331         CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13332                                             CE_PLAYER_ENTERS_X,
13333                                             player->index_bit, enter_side);
13334       }
13335
13336       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13337                                         CE_MOVE_OF_X, move_direction);
13338     }
13339
13340     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13341     {
13342       TestIfPlayerTouchesBadThing(jx, jy);
13343       TestIfPlayerTouchesCustomElement(jx, jy);
13344
13345       // needed because pushed element has not yet reached its destination,
13346       // so it would trigger a change event at its previous field location
13347       if (!player->is_pushing)
13348         TestIfElementTouchesCustomElement(jx, jy);      // for empty space
13349
13350       if (level.finish_dig_collect &&
13351           (player->is_digging || player->is_collecting))
13352       {
13353         int last_element = player->last_removed_element;
13354         int move_direction = player->MovDir;
13355         int enter_side = MV_DIR_OPPOSITE(move_direction);
13356         int change_event = (player->is_digging ? CE_PLAYER_DIGS_X :
13357                             CE_PLAYER_COLLECTS_X);
13358
13359         CheckTriggeredElementChangeByPlayer(jx, jy, last_element, change_event,
13360                                             player->index_bit, enter_side);
13361
13362         player->last_removed_element = EL_UNDEFINED;
13363       }
13364
13365       if (!player->active)
13366         RemovePlayer(player);
13367     }
13368
13369     if (level.use_step_counter)
13370       CheckLevelTime_StepCounter();
13371
13372     if (tape.single_step && tape.recording && !tape.pausing &&
13373         !player->programmed_action)
13374       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13375
13376     if (!player->programmed_action)
13377       CheckSaveEngineSnapshot(player);
13378   }
13379 }
13380
13381 void ScrollScreen(struct PlayerInfo *player, int mode)
13382 {
13383   static DelayCounter screen_frame_counter = { 0 };
13384
13385   if (mode == SCROLL_INIT)
13386   {
13387     // set scrolling step size according to actual player's moving speed
13388     ScrollStepSize = TILEX / player->move_delay_value;
13389
13390     screen_frame_counter.count = FrameCounter;
13391     screen_frame_counter.value = 1;
13392
13393     ScreenMovDir = player->MovDir;
13394     ScreenMovPos = player->MovPos;
13395     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13396     return;
13397   }
13398   else if (!FrameReached(&screen_frame_counter))
13399     return;
13400
13401   if (ScreenMovPos)
13402   {
13403     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13404     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13405     redraw_mask |= REDRAW_FIELD;
13406   }
13407   else
13408     ScreenMovDir = MV_NONE;
13409 }
13410
13411 void CheckNextToConditions(int x, int y)
13412 {
13413   int element = Tile[x][y];
13414
13415   if (IS_PLAYER(x, y))
13416     TestIfPlayerNextToCustomElement(x, y);
13417
13418   if (CAN_CHANGE_OR_HAS_ACTION(element) &&
13419       HAS_ANY_CHANGE_EVENT(element, CE_NEXT_TO_X))
13420     TestIfElementNextToCustomElement(x, y);
13421 }
13422
13423 void TestIfPlayerNextToCustomElement(int x, int y)
13424 {
13425   struct XY *xy = xy_topdown;
13426   static int trigger_sides[4][2] =
13427   {
13428     // center side       border side
13429     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13430     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13431     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13432     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13433   };
13434   int i;
13435
13436   if (!IS_PLAYER(x, y))
13437     return;
13438
13439   struct PlayerInfo *player = PLAYERINFO(x, y);
13440
13441   if (player->is_moving)
13442     return;
13443
13444   for (i = 0; i < NUM_DIRECTIONS; i++)
13445   {
13446     int xx = x + xy[i].x;
13447     int yy = y + xy[i].y;
13448     int border_side = trigger_sides[i][1];
13449     int border_element;
13450
13451     if (!IN_LEV_FIELD(xx, yy))
13452       continue;
13453
13454     if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
13455       continue;         // center and border element not connected
13456
13457     border_element = Tile[xx][yy];
13458
13459     CheckElementChangeByPlayer(xx, yy, border_element, CE_NEXT_TO_PLAYER,
13460                                player->index_bit, border_side);
13461     CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13462                                         CE_PLAYER_NEXT_TO_X,
13463                                         player->index_bit, border_side);
13464
13465     /* use player element that is initially defined in the level playfield,
13466        not the player element that corresponds to the runtime player number
13467        (example: a level that contains EL_PLAYER_3 as the only player would
13468        incorrectly give EL_PLAYER_1 for "player->element_nr") */
13469
13470     CheckElementChangeBySide(xx, yy, border_element, player->initial_element,
13471                              CE_NEXT_TO_X, border_side);
13472   }
13473 }
13474
13475 void TestIfPlayerTouchesCustomElement(int x, int y)
13476 {
13477   struct XY *xy = xy_topdown;
13478   static int trigger_sides[4][2] =
13479   {
13480     // center side       border side
13481     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13482     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13483     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13484     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13485   };
13486   static int touch_dir[4] =
13487   {
13488     MV_LEFT | MV_RIGHT,
13489     MV_UP   | MV_DOWN,
13490     MV_UP   | MV_DOWN,
13491     MV_LEFT | MV_RIGHT
13492   };
13493   int center_element = Tile[x][y];      // should always be non-moving!
13494   int i;
13495
13496   for (i = 0; i < NUM_DIRECTIONS; i++)
13497   {
13498     int xx = x + xy[i].x;
13499     int yy = y + xy[i].y;
13500     int center_side = trigger_sides[i][0];
13501     int border_side = trigger_sides[i][1];
13502     int border_element;
13503
13504     if (!IN_LEV_FIELD(xx, yy))
13505       continue;
13506
13507     if (IS_PLAYER(x, y))                // player found at center element
13508     {
13509       struct PlayerInfo *player = PLAYERINFO(x, y);
13510
13511       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13512         border_element = Tile[xx][yy];          // may be moving!
13513       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13514         border_element = Tile[xx][yy];
13515       else if (MovDir[xx][yy] & touch_dir[i])   // elements are touching
13516         border_element = MovingOrBlocked2Element(xx, yy);
13517       else
13518         continue;               // center and border element do not touch
13519
13520       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13521                                  player->index_bit, border_side);
13522       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13523                                           CE_PLAYER_TOUCHES_X,
13524                                           player->index_bit, border_side);
13525
13526       {
13527         /* use player element that is initially defined in the level playfield,
13528            not the player element that corresponds to the runtime player number
13529            (example: a level that contains EL_PLAYER_3 as the only player would
13530            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13531         int player_element = PLAYERINFO(x, y)->initial_element;
13532
13533         // as element "X" is the player here, check opposite (center) side
13534         CheckElementChangeBySide(xx, yy, border_element, player_element,
13535                                  CE_TOUCHING_X, center_side);
13536       }
13537     }
13538     else if (IS_PLAYER(xx, yy))         // player found at border element
13539     {
13540       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13541
13542       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13543       {
13544         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13545           continue;             // center and border element do not touch
13546       }
13547
13548       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13549                                  player->index_bit, center_side);
13550       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13551                                           CE_PLAYER_TOUCHES_X,
13552                                           player->index_bit, center_side);
13553
13554       {
13555         /* use player element that is initially defined in the level playfield,
13556            not the player element that corresponds to the runtime player number
13557            (example: a level that contains EL_PLAYER_3 as the only player would
13558            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13559         int player_element = PLAYERINFO(xx, yy)->initial_element;
13560
13561         // as element "X" is the player here, check opposite (border) side
13562         CheckElementChangeBySide(x, y, center_element, player_element,
13563                                  CE_TOUCHING_X, border_side);
13564       }
13565
13566       break;
13567     }
13568   }
13569 }
13570
13571 void TestIfElementNextToCustomElement(int x, int y)
13572 {
13573   struct XY *xy = xy_topdown;
13574   static int trigger_sides[4][2] =
13575   {
13576     // center side      border side
13577     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13578     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13579     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13580     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13581   };
13582   int center_element = Tile[x][y];      // should always be non-moving!
13583   int i;
13584
13585   if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
13586     return;
13587
13588   for (i = 0; i < NUM_DIRECTIONS; i++)
13589   {
13590     int xx = x + xy[i].x;
13591     int yy = y + xy[i].y;
13592     int border_side = trigger_sides[i][1];
13593     int border_element;
13594
13595     if (!IN_LEV_FIELD(xx, yy))
13596       continue;
13597
13598     if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
13599       continue;                 // center and border element not connected
13600
13601     border_element = Tile[xx][yy];
13602
13603     // check for change of center element (but change it only once)
13604     if (CheckElementChangeBySide(x, y, center_element, border_element,
13605                                  CE_NEXT_TO_X, border_side))
13606       break;
13607   }
13608 }
13609
13610 void TestIfElementTouchesCustomElement(int x, int y)
13611 {
13612   struct XY *xy = xy_topdown;
13613   static int trigger_sides[4][2] =
13614   {
13615     // center side      border side
13616     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13617     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13618     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13619     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13620   };
13621   static int touch_dir[4] =
13622   {
13623     MV_LEFT | MV_RIGHT,
13624     MV_UP   | MV_DOWN,
13625     MV_UP   | MV_DOWN,
13626     MV_LEFT | MV_RIGHT
13627   };
13628   boolean change_center_element = FALSE;
13629   int center_element = Tile[x][y];      // should always be non-moving!
13630   int border_element_old[NUM_DIRECTIONS];
13631   int i;
13632
13633   for (i = 0; i < NUM_DIRECTIONS; i++)
13634   {
13635     int xx = x + xy[i].x;
13636     int yy = y + xy[i].y;
13637     int border_element;
13638
13639     border_element_old[i] = -1;
13640
13641     if (!IN_LEV_FIELD(xx, yy))
13642       continue;
13643
13644     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13645       border_element = Tile[xx][yy];    // may be moving!
13646     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13647       border_element = Tile[xx][yy];
13648     else if (MovDir[xx][yy] & touch_dir[i])     // elements are touching
13649       border_element = MovingOrBlocked2Element(xx, yy);
13650     else
13651       continue;                 // center and border element do not touch
13652
13653     border_element_old[i] = border_element;
13654   }
13655
13656   for (i = 0; i < NUM_DIRECTIONS; i++)
13657   {
13658     int xx = x + xy[i].x;
13659     int yy = y + xy[i].y;
13660     int center_side = trigger_sides[i][0];
13661     int border_element = border_element_old[i];
13662
13663     if (border_element == -1)
13664       continue;
13665
13666     // check for change of border element
13667     CheckElementChangeBySide(xx, yy, border_element, center_element,
13668                              CE_TOUCHING_X, center_side);
13669
13670     // (center element cannot be player, so we don't have to check this here)
13671   }
13672
13673   for (i = 0; i < NUM_DIRECTIONS; i++)
13674   {
13675     int xx = x + xy[i].x;
13676     int yy = y + xy[i].y;
13677     int border_side = trigger_sides[i][1];
13678     int border_element = border_element_old[i];
13679
13680     if (border_element == -1)
13681       continue;
13682
13683     // check for change of center element (but change it only once)
13684     if (!change_center_element)
13685       change_center_element =
13686         CheckElementChangeBySide(x, y, center_element, border_element,
13687                                  CE_TOUCHING_X, border_side);
13688
13689     if (IS_PLAYER(xx, yy))
13690     {
13691       /* use player element that is initially defined in the level playfield,
13692          not the player element that corresponds to the runtime player number
13693          (example: a level that contains EL_PLAYER_3 as the only player would
13694          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13695       int player_element = PLAYERINFO(xx, yy)->initial_element;
13696
13697       // as element "X" is the player here, check opposite (border) side
13698       CheckElementChangeBySide(x, y, center_element, player_element,
13699                                CE_TOUCHING_X, border_side);
13700     }
13701   }
13702 }
13703
13704 void TestIfElementHitsCustomElement(int x, int y, int direction)
13705 {
13706   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13707   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13708   int hitx = x + dx, hity = y + dy;
13709   int hitting_element = Tile[x][y];
13710   int touched_element;
13711
13712   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13713     return;
13714
13715   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13716                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13717
13718   if (IN_LEV_FIELD(hitx, hity))
13719   {
13720     int opposite_direction = MV_DIR_OPPOSITE(direction);
13721     int hitting_side = direction;
13722     int touched_side = opposite_direction;
13723     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13724                           MovDir[hitx][hity] != direction ||
13725                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13726
13727     object_hit = TRUE;
13728
13729     if (object_hit)
13730     {
13731       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13732                                CE_HITTING_X, touched_side);
13733
13734       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13735                                CE_HIT_BY_X, hitting_side);
13736
13737       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13738                                CE_HIT_BY_SOMETHING, opposite_direction);
13739
13740       if (IS_PLAYER(hitx, hity))
13741       {
13742         /* use player element that is initially defined in the level playfield,
13743            not the player element that corresponds to the runtime player number
13744            (example: a level that contains EL_PLAYER_3 as the only player would
13745            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13746         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13747
13748         CheckElementChangeBySide(x, y, hitting_element, player_element,
13749                                  CE_HITTING_X, touched_side);
13750       }
13751     }
13752   }
13753
13754   // "hitting something" is also true when hitting the playfield border
13755   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13756                            CE_HITTING_SOMETHING, direction);
13757 }
13758
13759 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13760 {
13761   int i, kill_x = -1, kill_y = -1;
13762
13763   int bad_element = -1;
13764   struct XY *test_xy = xy_topdown;
13765   static int test_dir[4] =
13766   {
13767     MV_UP,
13768     MV_LEFT,
13769     MV_RIGHT,
13770     MV_DOWN
13771   };
13772
13773   for (i = 0; i < NUM_DIRECTIONS; i++)
13774   {
13775     int test_x, test_y, test_move_dir, test_element;
13776
13777     test_x = good_x + test_xy[i].x;
13778     test_y = good_y + test_xy[i].y;
13779
13780     if (!IN_LEV_FIELD(test_x, test_y))
13781       continue;
13782
13783     test_move_dir =
13784       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13785
13786     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13787
13788     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13789        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13790     */
13791     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13792         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13793     {
13794       kill_x = test_x;
13795       kill_y = test_y;
13796       bad_element = test_element;
13797
13798       break;
13799     }
13800   }
13801
13802   if (kill_x != -1 || kill_y != -1)
13803   {
13804     if (IS_PLAYER(good_x, good_y))
13805     {
13806       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13807
13808       if (player->shield_deadly_time_left > 0 &&
13809           !IS_INDESTRUCTIBLE(bad_element))
13810         Bang(kill_x, kill_y);
13811       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13812         KillPlayer(player);
13813     }
13814     else
13815       Bang(good_x, good_y);
13816   }
13817 }
13818
13819 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13820 {
13821   int i, kill_x = -1, kill_y = -1;
13822   int bad_element = Tile[bad_x][bad_y];
13823   struct XY *test_xy = xy_topdown;
13824   static int touch_dir[4] =
13825   {
13826     MV_LEFT | MV_RIGHT,
13827     MV_UP   | MV_DOWN,
13828     MV_UP   | MV_DOWN,
13829     MV_LEFT | MV_RIGHT
13830   };
13831   static int test_dir[4] =
13832   {
13833     MV_UP,
13834     MV_LEFT,
13835     MV_RIGHT,
13836     MV_DOWN
13837   };
13838
13839   if (bad_element == EL_EXPLOSION)      // skip just exploding bad things
13840     return;
13841
13842   for (i = 0; i < NUM_DIRECTIONS; i++)
13843   {
13844     int test_x, test_y, test_move_dir, test_element;
13845
13846     test_x = bad_x + test_xy[i].x;
13847     test_y = bad_y + test_xy[i].y;
13848
13849     if (!IN_LEV_FIELD(test_x, test_y))
13850       continue;
13851
13852     test_move_dir =
13853       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13854
13855     test_element = Tile[test_x][test_y];
13856
13857     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13858        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13859     */
13860     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13861         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13862     {
13863       // good thing is player or penguin that does not move away
13864       if (IS_PLAYER(test_x, test_y))
13865       {
13866         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13867
13868         if (bad_element == EL_ROBOT && player->is_moving)
13869           continue;     // robot does not kill player if he is moving
13870
13871         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13872         {
13873           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13874             continue;           // center and border element do not touch
13875         }
13876
13877         kill_x = test_x;
13878         kill_y = test_y;
13879
13880         break;
13881       }
13882       else if (test_element == EL_PENGUIN)
13883       {
13884         kill_x = test_x;
13885         kill_y = test_y;
13886
13887         break;
13888       }
13889     }
13890   }
13891
13892   if (kill_x != -1 || kill_y != -1)
13893   {
13894     if (IS_PLAYER(kill_x, kill_y))
13895     {
13896       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13897
13898       if (player->shield_deadly_time_left > 0 &&
13899           !IS_INDESTRUCTIBLE(bad_element))
13900         Bang(bad_x, bad_y);
13901       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13902         KillPlayer(player);
13903     }
13904     else
13905       Bang(kill_x, kill_y);
13906   }
13907 }
13908
13909 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13910 {
13911   int bad_element = Tile[bad_x][bad_y];
13912   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13913   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13914   int test_x = bad_x + dx, test_y = bad_y + dy;
13915   int test_move_dir, test_element;
13916   int kill_x = -1, kill_y = -1;
13917
13918   if (!IN_LEV_FIELD(test_x, test_y))
13919     return;
13920
13921   test_move_dir =
13922     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13923
13924   test_element = Tile[test_x][test_y];
13925
13926   if (test_move_dir != bad_move_dir)
13927   {
13928     // good thing can be player or penguin that does not move away
13929     if (IS_PLAYER(test_x, test_y))
13930     {
13931       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13932
13933       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13934          player as being hit when he is moving towards the bad thing, because
13935          the "get hit by" condition would be lost after the player stops) */
13936       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13937         return;         // player moves away from bad thing
13938
13939       kill_x = test_x;
13940       kill_y = test_y;
13941     }
13942     else if (test_element == EL_PENGUIN)
13943     {
13944       kill_x = test_x;
13945       kill_y = test_y;
13946     }
13947   }
13948
13949   if (kill_x != -1 || kill_y != -1)
13950   {
13951     if (IS_PLAYER(kill_x, kill_y))
13952     {
13953       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13954
13955       if (player->shield_deadly_time_left > 0 &&
13956           !IS_INDESTRUCTIBLE(bad_element))
13957         Bang(bad_x, bad_y);
13958       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13959         KillPlayer(player);
13960     }
13961     else
13962       Bang(kill_x, kill_y);
13963   }
13964 }
13965
13966 void TestIfPlayerTouchesBadThing(int x, int y)
13967 {
13968   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13969 }
13970
13971 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13972 {
13973   TestIfGoodThingHitsBadThing(x, y, move_dir);
13974 }
13975
13976 void TestIfBadThingTouchesPlayer(int x, int y)
13977 {
13978   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13979 }
13980
13981 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13982 {
13983   TestIfBadThingHitsGoodThing(x, y, move_dir);
13984 }
13985
13986 void TestIfFriendTouchesBadThing(int x, int y)
13987 {
13988   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13989 }
13990
13991 void TestIfBadThingTouchesFriend(int x, int y)
13992 {
13993   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13994 }
13995
13996 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13997 {
13998   int i, kill_x = bad_x, kill_y = bad_y;
13999   struct XY *xy = xy_topdown;
14000
14001   for (i = 0; i < NUM_DIRECTIONS; i++)
14002   {
14003     int x, y, element;
14004
14005     x = bad_x + xy[i].x;
14006     y = bad_y + xy[i].y;
14007     if (!IN_LEV_FIELD(x, y))
14008       continue;
14009
14010     element = Tile[x][y];
14011     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
14012         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
14013     {
14014       kill_x = x;
14015       kill_y = y;
14016       break;
14017     }
14018   }
14019
14020   if (kill_x != bad_x || kill_y != bad_y)
14021     Bang(bad_x, bad_y);
14022 }
14023
14024 void KillPlayer(struct PlayerInfo *player)
14025 {
14026   int jx = player->jx, jy = player->jy;
14027
14028   if (!player->active)
14029     return;
14030
14031 #if 0
14032   Debug("game:playing:KillPlayer",
14033         "0: killed == %d, active == %d, reanimated == %d",
14034         player->killed, player->active, player->reanimated);
14035 #endif
14036
14037   /* the following code was introduced to prevent an infinite loop when calling
14038      -> Bang()
14039      -> CheckTriggeredElementChangeExt()
14040      -> ExecuteCustomElementAction()
14041      -> KillPlayer()
14042      -> (infinitely repeating the above sequence of function calls)
14043      which occurs when killing the player while having a CE with the setting
14044      "kill player X when explosion of <player X>"; the solution using a new
14045      field "player->killed" was chosen for backwards compatibility, although
14046      clever use of the fields "player->active" etc. would probably also work */
14047 #if 1
14048   if (player->killed)
14049     return;
14050 #endif
14051
14052   player->killed = TRUE;
14053
14054   // remove accessible field at the player's position
14055   RemoveField(jx, jy);
14056
14057   // deactivate shield (else Bang()/Explode() would not work right)
14058   player->shield_normal_time_left = 0;
14059   player->shield_deadly_time_left = 0;
14060
14061 #if 0
14062   Debug("game:playing:KillPlayer",
14063         "1: killed == %d, active == %d, reanimated == %d",
14064         player->killed, player->active, player->reanimated);
14065 #endif
14066
14067   Bang(jx, jy);
14068
14069 #if 0
14070   Debug("game:playing:KillPlayer",
14071         "2: killed == %d, active == %d, reanimated == %d",
14072         player->killed, player->active, player->reanimated);
14073 #endif
14074
14075   if (player->reanimated)       // killed player may have been reanimated
14076     player->killed = player->reanimated = FALSE;
14077   else
14078     BuryPlayer(player);
14079 }
14080
14081 static void KillPlayerUnlessEnemyProtected(int x, int y)
14082 {
14083   if (!PLAYER_ENEMY_PROTECTED(x, y))
14084     KillPlayer(PLAYERINFO(x, y));
14085 }
14086
14087 static void KillPlayerUnlessExplosionProtected(int x, int y)
14088 {
14089   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14090     KillPlayer(PLAYERINFO(x, y));
14091 }
14092
14093 void BuryPlayer(struct PlayerInfo *player)
14094 {
14095   int jx = player->jx, jy = player->jy;
14096
14097   if (!player->active)
14098     return;
14099
14100   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14101
14102   RemovePlayer(player);
14103
14104   player->buried = TRUE;
14105
14106   if (game.all_players_gone)
14107     game.GameOver = TRUE;
14108 }
14109
14110 void RemovePlayer(struct PlayerInfo *player)
14111 {
14112   int jx = player->jx, jy = player->jy;
14113   int i, found = FALSE;
14114
14115   player->present = FALSE;
14116   player->active = FALSE;
14117
14118   // required for some CE actions (even if the player is not active anymore)
14119   player->MovPos = 0;
14120
14121   if (!ExplodeField[jx][jy])
14122     StorePlayer[jx][jy] = 0;
14123
14124   if (player->is_moving)
14125     TEST_DrawLevelField(player->last_jx, player->last_jy);
14126
14127   for (i = 0; i < MAX_PLAYERS; i++)
14128     if (stored_player[i].active)
14129       found = TRUE;
14130
14131   if (!found)
14132   {
14133     game.all_players_gone = TRUE;
14134     game.GameOver = TRUE;
14135   }
14136
14137   game.exit_x = game.robot_wheel_x = jx;
14138   game.exit_y = game.robot_wheel_y = jy;
14139 }
14140
14141 void ExitPlayer(struct PlayerInfo *player)
14142 {
14143   DrawPlayer(player);   // needed here only to cleanup last field
14144   RemovePlayer(player);
14145
14146   if (game.players_still_needed > 0)
14147     game.players_still_needed--;
14148 }
14149
14150 static void SetFieldForSnapping(int x, int y, int element, int direction,
14151                                 int player_index_bit)
14152 {
14153   struct ElementInfo *ei = &element_info[element];
14154   int direction_bit = MV_DIR_TO_BIT(direction);
14155   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14156   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14157                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14158
14159   Tile[x][y] = EL_ELEMENT_SNAPPING;
14160   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14161   MovDir[x][y] = direction;
14162   Store[x][y] = element;
14163   Store2[x][y] = player_index_bit;
14164
14165   ResetGfxAnimation(x, y);
14166
14167   GfxElement[x][y] = element;
14168   GfxAction[x][y] = action;
14169   GfxDir[x][y] = direction;
14170   GfxFrame[x][y] = -1;
14171 }
14172
14173 static void TestFieldAfterSnapping(int x, int y, int element, int direction,
14174                                    int player_index_bit)
14175 {
14176   TestIfElementTouchesCustomElement(x, y);      // for empty space
14177
14178   if (level.finish_dig_collect)
14179   {
14180     int dig_side = MV_DIR_OPPOSITE(direction);
14181     int change_event = (IS_DIGGABLE(element) ? CE_PLAYER_DIGS_X :
14182                         CE_PLAYER_COLLECTS_X);
14183
14184     CheckTriggeredElementChangeByPlayer(x, y, element, change_event,
14185                                         player_index_bit, dig_side);
14186     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14187                                         player_index_bit, dig_side);
14188   }
14189 }
14190
14191 /*
14192   =============================================================================
14193   checkDiagonalPushing()
14194   -----------------------------------------------------------------------------
14195   check if diagonal input device direction results in pushing of object
14196   (by checking if the alternative direction is walkable, diggable, ...)
14197   =============================================================================
14198 */
14199
14200 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14201                                     int x, int y, int real_dx, int real_dy)
14202 {
14203   int jx, jy, dx, dy, xx, yy;
14204
14205   if (real_dx == 0 || real_dy == 0)     // no diagonal direction => push
14206     return TRUE;
14207
14208   // diagonal direction: check alternative direction
14209   jx = player->jx;
14210   jy = player->jy;
14211   dx = x - jx;
14212   dy = y - jy;
14213   xx = jx + (dx == 0 ? real_dx : 0);
14214   yy = jy + (dy == 0 ? real_dy : 0);
14215
14216   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Tile[xx][yy]));
14217 }
14218
14219 /*
14220   =============================================================================
14221   DigField()
14222   -----------------------------------------------------------------------------
14223   x, y:                 field next to player (non-diagonal) to try to dig to
14224   real_dx, real_dy:     direction as read from input device (can be diagonal)
14225   =============================================================================
14226 */
14227
14228 static int DigField(struct PlayerInfo *player,
14229                     int oldx, int oldy, int x, int y,
14230                     int real_dx, int real_dy, int mode)
14231 {
14232   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14233   boolean player_was_pushing = player->is_pushing;
14234   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14235   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14236   int jx = oldx, jy = oldy;
14237   int dx = x - jx, dy = y - jy;
14238   int nextx = x + dx, nexty = y + dy;
14239   int move_direction = (dx == -1 ? MV_LEFT  :
14240                         dx == +1 ? MV_RIGHT :
14241                         dy == -1 ? MV_UP    :
14242                         dy == +1 ? MV_DOWN  : MV_NONE);
14243   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14244   int dig_side = MV_DIR_OPPOSITE(move_direction);
14245   int old_element = Tile[jx][jy];
14246   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14247   int collect_count;
14248
14249   if (is_player)                // function can also be called by EL_PENGUIN
14250   {
14251     if (player->MovPos == 0)
14252     {
14253       player->is_digging = FALSE;
14254       player->is_collecting = FALSE;
14255     }
14256
14257     if (player->MovPos == 0)    // last pushing move finished
14258       player->is_pushing = FALSE;
14259
14260     if (mode == DF_NO_PUSH)     // player just stopped pushing
14261     {
14262       player->is_switching = FALSE;
14263       player->push_delay = -1;
14264
14265       return MP_NO_ACTION;
14266     }
14267   }
14268   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14269     old_element = Back[jx][jy];
14270
14271   // in case of element dropped at player position, check background
14272   else if (Back[jx][jy] != EL_EMPTY &&
14273            game.engine_version >= VERSION_IDENT(2,2,0,0))
14274     old_element = Back[jx][jy];
14275
14276   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14277     return MP_NO_ACTION;        // field has no opening in this direction
14278
14279   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element, opposite_direction))
14280     return MP_NO_ACTION;        // field has no opening in this direction
14281
14282   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14283   {
14284     SplashAcid(x, y);
14285
14286     Tile[jx][jy] = player->artwork_element;
14287     InitMovingField(jx, jy, MV_DOWN);
14288     Store[jx][jy] = EL_ACID;
14289     ContinueMoving(jx, jy);
14290     BuryPlayer(player);
14291
14292     return MP_DONT_RUN_INTO;
14293   }
14294
14295   if (player_can_move && DONT_RUN_INTO(element))
14296   {
14297     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14298
14299     return MP_DONT_RUN_INTO;
14300   }
14301
14302   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14303     return MP_NO_ACTION;
14304
14305   collect_count = element_info[element].collect_count_initial;
14306
14307   if (!is_player && !IS_COLLECTIBLE(element))   // penguin cannot collect it
14308     return MP_NO_ACTION;
14309
14310   if (game.engine_version < VERSION_IDENT(2,2,0,0))
14311     player_can_move = player_can_move_or_snap;
14312
14313   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14314       game.engine_version >= VERSION_IDENT(2,2,0,0))
14315   {
14316     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14317                                player->index_bit, dig_side);
14318     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14319                                         player->index_bit, dig_side);
14320
14321     if (element == EL_DC_LANDMINE)
14322       Bang(x, y);
14323
14324     if (Tile[x][y] != element)          // field changed by snapping
14325       return MP_ACTION;
14326
14327     return MP_NO_ACTION;
14328   }
14329
14330   if (player->gravity && is_player && !player->is_auto_moving &&
14331       canFallDown(player) && move_direction != MV_DOWN &&
14332       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14333     return MP_NO_ACTION;        // player cannot walk here due to gravity
14334
14335   if (player_can_move &&
14336       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14337   {
14338     int sound_element = SND_ELEMENT(element);
14339     int sound_action = ACTION_WALKING;
14340
14341     if (IS_RND_GATE(element))
14342     {
14343       if (!player->key[RND_GATE_NR(element)])
14344         return MP_NO_ACTION;
14345     }
14346     else if (IS_RND_GATE_GRAY(element))
14347     {
14348       if (!player->key[RND_GATE_GRAY_NR(element)])
14349         return MP_NO_ACTION;
14350     }
14351     else if (IS_RND_GATE_GRAY_ACTIVE(element))
14352     {
14353       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14354         return MP_NO_ACTION;
14355     }
14356     else if (element == EL_EXIT_OPEN ||
14357              element == EL_EM_EXIT_OPEN ||
14358              element == EL_EM_EXIT_OPENING ||
14359              element == EL_STEEL_EXIT_OPEN ||
14360              element == EL_EM_STEEL_EXIT_OPEN ||
14361              element == EL_EM_STEEL_EXIT_OPENING ||
14362              element == EL_SP_EXIT_OPEN ||
14363              element == EL_SP_EXIT_OPENING)
14364     {
14365       sound_action = ACTION_PASSING;    // player is passing exit
14366     }
14367     else if (element == EL_EMPTY)
14368     {
14369       sound_action = ACTION_MOVING;             // nothing to walk on
14370     }
14371
14372     // play sound from background or player, whatever is available
14373     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14374       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14375     else
14376       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14377   }
14378   else if (player_can_move &&
14379            IS_PASSABLE(element) && canPassField(x, y, move_direction))
14380   {
14381     if (!ACCESS_FROM(element, opposite_direction))
14382       return MP_NO_ACTION;      // field not accessible from this direction
14383
14384     if (CAN_MOVE(element))      // only fixed elements can be passed!
14385       return MP_NO_ACTION;
14386
14387     if (IS_EM_GATE(element))
14388     {
14389       if (!player->key[EM_GATE_NR(element)])
14390         return MP_NO_ACTION;
14391     }
14392     else if (IS_EM_GATE_GRAY(element))
14393     {
14394       if (!player->key[EM_GATE_GRAY_NR(element)])
14395         return MP_NO_ACTION;
14396     }
14397     else if (IS_EM_GATE_GRAY_ACTIVE(element))
14398     {
14399       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14400         return MP_NO_ACTION;
14401     }
14402     else if (IS_EMC_GATE(element))
14403     {
14404       if (!player->key[EMC_GATE_NR(element)])
14405         return MP_NO_ACTION;
14406     }
14407     else if (IS_EMC_GATE_GRAY(element))
14408     {
14409       if (!player->key[EMC_GATE_GRAY_NR(element)])
14410         return MP_NO_ACTION;
14411     }
14412     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14413     {
14414       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14415         return MP_NO_ACTION;
14416     }
14417     else if (element == EL_DC_GATE_WHITE ||
14418              element == EL_DC_GATE_WHITE_GRAY ||
14419              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14420     {
14421       if (player->num_white_keys == 0)
14422         return MP_NO_ACTION;
14423
14424       player->num_white_keys--;
14425     }
14426     else if (IS_SP_PORT(element))
14427     {
14428       if (element == EL_SP_GRAVITY_PORT_LEFT ||
14429           element == EL_SP_GRAVITY_PORT_RIGHT ||
14430           element == EL_SP_GRAVITY_PORT_UP ||
14431           element == EL_SP_GRAVITY_PORT_DOWN)
14432         player->gravity = !player->gravity;
14433       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14434                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14435                element == EL_SP_GRAVITY_ON_PORT_UP ||
14436                element == EL_SP_GRAVITY_ON_PORT_DOWN)
14437         player->gravity = TRUE;
14438       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14439                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14440                element == EL_SP_GRAVITY_OFF_PORT_UP ||
14441                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14442         player->gravity = FALSE;
14443     }
14444
14445     // automatically move to the next field with double speed
14446     player->programmed_action = move_direction;
14447
14448     if (player->move_delay_reset_counter == 0)
14449     {
14450       player->move_delay_reset_counter = 2;     // two double speed steps
14451
14452       DOUBLE_PLAYER_SPEED(player);
14453     }
14454
14455     PlayLevelSoundAction(x, y, ACTION_PASSING);
14456   }
14457   else if (player_can_move_or_snap && IS_DIGGABLE(element))
14458   {
14459     RemoveField(x, y);
14460
14461     if (mode != DF_SNAP)
14462     {
14463       GfxElement[x][y] = GFX_ELEMENT(element);
14464       player->is_digging = TRUE;
14465     }
14466
14467     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14468
14469     // use old behaviour for old levels (digging)
14470     if (!level.finish_dig_collect)
14471     {
14472       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14473                                           player->index_bit, dig_side);
14474
14475       // if digging triggered player relocation, finish digging tile
14476       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14477         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14478     }
14479
14480     if (mode == DF_SNAP)
14481     {
14482       if (level.block_snap_field)
14483         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14484       else
14485         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14486
14487       // use old behaviour for old levels (snapping)
14488       if (!level.finish_dig_collect)
14489         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14490                                             player->index_bit, dig_side);
14491     }
14492   }
14493   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14494   {
14495     RemoveField(x, y);
14496
14497     if (is_player && mode != DF_SNAP)
14498     {
14499       GfxElement[x][y] = element;
14500       player->is_collecting = TRUE;
14501     }
14502
14503     if (element == EL_SPEED_PILL)
14504     {
14505       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14506     }
14507     else if (element == EL_EXTRA_TIME && level.time > 0)
14508     {
14509       TimeLeft += level.extra_time;
14510
14511       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14512
14513       DisplayGameControlValues();
14514     }
14515     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14516     {
14517       int shield_time = (element == EL_SHIELD_DEADLY ?
14518                          level.shield_deadly_time :
14519                          level.shield_normal_time);
14520
14521       player->shield_normal_time_left += shield_time;
14522       if (element == EL_SHIELD_DEADLY)
14523         player->shield_deadly_time_left += shield_time;
14524     }
14525     else if (element == EL_DYNAMITE ||
14526              element == EL_EM_DYNAMITE ||
14527              element == EL_SP_DISK_RED)
14528     {
14529       if (player->inventory_size < MAX_INVENTORY_SIZE)
14530         player->inventory_element[player->inventory_size++] = element;
14531
14532       DrawGameDoorValues();
14533     }
14534     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14535     {
14536       player->dynabomb_count++;
14537       player->dynabombs_left++;
14538     }
14539     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14540     {
14541       player->dynabomb_size++;
14542     }
14543     else if (element == EL_DYNABOMB_INCREASE_POWER)
14544     {
14545       player->dynabomb_xl = TRUE;
14546     }
14547     else if (IS_KEY(element))
14548     {
14549       player->key[KEY_NR(element)] = TRUE;
14550
14551       DrawGameDoorValues();
14552     }
14553     else if (element == EL_DC_KEY_WHITE)
14554     {
14555       player->num_white_keys++;
14556
14557       // display white keys?
14558       // DrawGameDoorValues();
14559     }
14560     else if (IS_ENVELOPE(element))
14561     {
14562       boolean wait_for_snapping = (mode == DF_SNAP && level.block_snap_field);
14563
14564       if (!wait_for_snapping)
14565         player->show_envelope = element;
14566     }
14567     else if (element == EL_EMC_LENSES)
14568     {
14569       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14570
14571       RedrawAllInvisibleElementsForLenses();
14572     }
14573     else if (element == EL_EMC_MAGNIFIER)
14574     {
14575       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14576
14577       RedrawAllInvisibleElementsForMagnifier();
14578     }
14579     else if (IS_DROPPABLE(element) ||
14580              IS_THROWABLE(element))     // can be collected and dropped
14581     {
14582       int i;
14583
14584       if (collect_count == 0)
14585         player->inventory_infinite_element = element;
14586       else
14587         for (i = 0; i < collect_count; i++)
14588           if (player->inventory_size < MAX_INVENTORY_SIZE)
14589             player->inventory_element[player->inventory_size++] = element;
14590
14591       DrawGameDoorValues();
14592     }
14593     else if (collect_count > 0)
14594     {
14595       game.gems_still_needed -= collect_count;
14596       if (game.gems_still_needed < 0)
14597         game.gems_still_needed = 0;
14598
14599       game.snapshot.collected_item = TRUE;
14600
14601       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
14602
14603       DisplayGameControlValues();
14604     }
14605
14606     RaiseScoreElement(element);
14607     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14608
14609     // use old behaviour for old levels (collecting)
14610     if (!level.finish_dig_collect && is_player)
14611     {
14612       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14613                                           player->index_bit, dig_side);
14614
14615       // if collecting triggered player relocation, finish collecting tile
14616       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14617         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14618     }
14619
14620     if (mode == DF_SNAP)
14621     {
14622       if (level.block_snap_field)
14623         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14624       else
14625         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14626
14627       // use old behaviour for old levels (snapping)
14628       if (!level.finish_dig_collect)
14629         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14630                                             player->index_bit, dig_side);
14631     }
14632   }
14633   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14634   {
14635     if (mode == DF_SNAP && element != EL_BD_ROCK)
14636       return MP_NO_ACTION;
14637
14638     if (CAN_FALL(element) && dy)
14639       return MP_NO_ACTION;
14640
14641     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14642         !(element == EL_SPRING && level.use_spring_bug))
14643       return MP_NO_ACTION;
14644
14645     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14646         ((move_direction & MV_VERTICAL &&
14647           ((element_info[element].move_pattern & MV_LEFT &&
14648             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14649            (element_info[element].move_pattern & MV_RIGHT &&
14650             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14651          (move_direction & MV_HORIZONTAL &&
14652           ((element_info[element].move_pattern & MV_UP &&
14653             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14654            (element_info[element].move_pattern & MV_DOWN &&
14655             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14656       return MP_NO_ACTION;
14657
14658     // do not push elements already moving away faster than player
14659     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14660         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14661       return MP_NO_ACTION;
14662
14663     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14664     {
14665       if (player->push_delay_value == -1 || !player_was_pushing)
14666         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14667     }
14668     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14669     {
14670       if (player->push_delay_value == -1)
14671         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14672     }
14673     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14674     {
14675       if (!player->is_pushing)
14676         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14677     }
14678
14679     player->is_pushing = TRUE;
14680     player->is_active = TRUE;
14681
14682     if (!(IN_LEV_FIELD(nextx, nexty) &&
14683           (IS_FREE(nextx, nexty) ||
14684            (IS_SB_ELEMENT(element) &&
14685             Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14686            (IS_CUSTOM_ELEMENT(element) &&
14687             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
14688       return MP_NO_ACTION;
14689
14690     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14691       return MP_NO_ACTION;
14692
14693     if (player->push_delay == -1)       // new pushing; restart delay
14694       player->push_delay = 0;
14695
14696     if (player->push_delay < player->push_delay_value &&
14697         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14698         element != EL_SPRING && element != EL_BALLOON)
14699     {
14700       // make sure that there is no move delay before next try to push
14701       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14702         player->move_delay = 0;
14703
14704       return MP_NO_ACTION;
14705     }
14706
14707     if (IS_CUSTOM_ELEMENT(element) &&
14708         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14709     {
14710       if (!DigFieldByCE(nextx, nexty, element))
14711         return MP_NO_ACTION;
14712     }
14713
14714     if (IS_SB_ELEMENT(element))
14715     {
14716       boolean sokoban_task_solved = FALSE;
14717
14718       if (element == EL_SOKOBAN_FIELD_FULL)
14719       {
14720         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14721
14722         IncrementSokobanFieldsNeeded();
14723         IncrementSokobanObjectsNeeded();
14724       }
14725
14726       if (Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14727       {
14728         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14729
14730         DecrementSokobanFieldsNeeded();
14731         DecrementSokobanObjectsNeeded();
14732
14733         // sokoban object was pushed from empty field to sokoban field
14734         if (Back[x][y] == EL_EMPTY)
14735           sokoban_task_solved = TRUE;
14736       }
14737
14738       Tile[x][y] = EL_SOKOBAN_OBJECT;
14739
14740       if (Back[x][y] == Back[nextx][nexty])
14741         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14742       else if (Back[x][y] != 0)
14743         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14744                                     ACTION_EMPTYING);
14745       else
14746         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14747                                     ACTION_FILLING);
14748
14749       if (sokoban_task_solved &&
14750           game.sokoban_fields_still_needed == 0 &&
14751           game.sokoban_objects_still_needed == 0 &&
14752           level.auto_exit_sokoban)
14753       {
14754         game.players_still_needed = 0;
14755
14756         LevelSolved();
14757
14758         PlaySound(SND_GAME_SOKOBAN_SOLVING);
14759       }
14760     }
14761     else
14762       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14763
14764     InitMovingField(x, y, move_direction);
14765     GfxAction[x][y] = ACTION_PUSHING;
14766
14767     if (mode == DF_SNAP)
14768       ContinueMoving(x, y);
14769     else
14770       MovPos[x][y] = (dx != 0 ? dx : dy);
14771
14772     Pushed[x][y] = TRUE;
14773     Pushed[nextx][nexty] = TRUE;
14774
14775     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14776       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14777     else
14778       player->push_delay_value = -1;    // get new value later
14779
14780     // check for element change _after_ element has been pushed
14781     if (game.use_change_when_pushing_bug)
14782     {
14783       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14784                                  player->index_bit, dig_side);
14785       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14786                                           player->index_bit, dig_side);
14787     }
14788   }
14789   else if (IS_SWITCHABLE(element))
14790   {
14791     if (PLAYER_SWITCHING(player, x, y))
14792     {
14793       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14794                                           player->index_bit, dig_side);
14795
14796       return MP_ACTION;
14797     }
14798
14799     player->is_switching = TRUE;
14800     player->switch_x = x;
14801     player->switch_y = y;
14802
14803     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14804
14805     if (element == EL_ROBOT_WHEEL)
14806     {
14807       Tile[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14808
14809       game.robot_wheel_x = x;
14810       game.robot_wheel_y = y;
14811       game.robot_wheel_active = TRUE;
14812
14813       TEST_DrawLevelField(x, y);
14814     }
14815     else if (element == EL_SP_TERMINAL)
14816     {
14817       int xx, yy;
14818
14819       SCAN_PLAYFIELD(xx, yy)
14820       {
14821         if (Tile[xx][yy] == EL_SP_DISK_YELLOW)
14822         {
14823           Bang(xx, yy);
14824         }
14825         else if (Tile[xx][yy] == EL_SP_TERMINAL)
14826         {
14827           Tile[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14828
14829           ResetGfxAnimation(xx, yy);
14830           TEST_DrawLevelField(xx, yy);
14831         }
14832       }
14833     }
14834     else if (IS_BELT_SWITCH(element))
14835     {
14836       ToggleBeltSwitch(x, y);
14837     }
14838     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14839              element == EL_SWITCHGATE_SWITCH_DOWN ||
14840              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14841              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14842     {
14843       ToggleSwitchgateSwitch();
14844     }
14845     else if (element == EL_LIGHT_SWITCH ||
14846              element == EL_LIGHT_SWITCH_ACTIVE)
14847     {
14848       ToggleLightSwitch(x, y);
14849     }
14850     else if (element == EL_TIMEGATE_SWITCH ||
14851              element == EL_DC_TIMEGATE_SWITCH)
14852     {
14853       ActivateTimegateSwitch(x, y);
14854     }
14855     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14856              element == EL_BALLOON_SWITCH_RIGHT ||
14857              element == EL_BALLOON_SWITCH_UP    ||
14858              element == EL_BALLOON_SWITCH_DOWN  ||
14859              element == EL_BALLOON_SWITCH_NONE  ||
14860              element == EL_BALLOON_SWITCH_ANY)
14861     {
14862       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14863                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14864                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14865                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14866                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14867                              move_direction);
14868     }
14869     else if (element == EL_LAMP)
14870     {
14871       Tile[x][y] = EL_LAMP_ACTIVE;
14872       game.lights_still_needed--;
14873
14874       ResetGfxAnimation(x, y);
14875       TEST_DrawLevelField(x, y);
14876     }
14877     else if (element == EL_TIME_ORB_FULL)
14878     {
14879       Tile[x][y] = EL_TIME_ORB_EMPTY;
14880
14881       if (level.time > 0 || level.use_time_orb_bug)
14882       {
14883         TimeLeft += level.time_orb_time;
14884         game.no_level_time_limit = FALSE;
14885
14886         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14887
14888         DisplayGameControlValues();
14889       }
14890
14891       ResetGfxAnimation(x, y);
14892       TEST_DrawLevelField(x, y);
14893     }
14894     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14895              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14896     {
14897       int xx, yy;
14898
14899       game.ball_active = !game.ball_active;
14900
14901       SCAN_PLAYFIELD(xx, yy)
14902       {
14903         int e = Tile[xx][yy];
14904
14905         if (game.ball_active)
14906         {
14907           if (e == EL_EMC_MAGIC_BALL)
14908             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14909           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14910             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14911         }
14912         else
14913         {
14914           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14915             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14916           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14917             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14918         }
14919       }
14920     }
14921
14922     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14923                                         player->index_bit, dig_side);
14924
14925     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14926                                         player->index_bit, dig_side);
14927
14928     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14929                                         player->index_bit, dig_side);
14930
14931     return MP_ACTION;
14932   }
14933   else
14934   {
14935     if (!PLAYER_SWITCHING(player, x, y))
14936     {
14937       player->is_switching = TRUE;
14938       player->switch_x = x;
14939       player->switch_y = y;
14940
14941       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14942                                  player->index_bit, dig_side);
14943       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14944                                           player->index_bit, dig_side);
14945
14946       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14947                                  player->index_bit, dig_side);
14948       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14949                                           player->index_bit, dig_side);
14950     }
14951
14952     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14953                                player->index_bit, dig_side);
14954     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14955                                         player->index_bit, dig_side);
14956
14957     return MP_NO_ACTION;
14958   }
14959
14960   player->push_delay = -1;
14961
14962   if (is_player)                // function can also be called by EL_PENGUIN
14963   {
14964     if (Tile[x][y] != element)          // really digged/collected something
14965     {
14966       player->is_collecting = !player->is_digging;
14967       player->is_active = TRUE;
14968
14969       player->last_removed_element = element;
14970     }
14971   }
14972
14973   return MP_MOVING;
14974 }
14975
14976 static boolean DigFieldByCE(int x, int y, int digging_element)
14977 {
14978   int element = Tile[x][y];
14979
14980   if (!IS_FREE(x, y))
14981   {
14982     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14983                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14984                   ACTION_BREAKING);
14985
14986     // no element can dig solid indestructible elements
14987     if (IS_INDESTRUCTIBLE(element) &&
14988         !IS_DIGGABLE(element) &&
14989         !IS_COLLECTIBLE(element))
14990       return FALSE;
14991
14992     if (AmoebaNr[x][y] &&
14993         (element == EL_AMOEBA_FULL ||
14994          element == EL_BD_AMOEBA ||
14995          element == EL_AMOEBA_GROWING))
14996     {
14997       AmoebaCnt[AmoebaNr[x][y]]--;
14998       AmoebaCnt2[AmoebaNr[x][y]]--;
14999     }
15000
15001     if (IS_MOVING(x, y))
15002       RemoveMovingField(x, y);
15003     else
15004     {
15005       RemoveField(x, y);
15006       TEST_DrawLevelField(x, y);
15007     }
15008
15009     // if digged element was about to explode, prevent the explosion
15010     ExplodeField[x][y] = EX_TYPE_NONE;
15011
15012     PlayLevelSoundAction(x, y, action);
15013   }
15014
15015   Store[x][y] = EL_EMPTY;
15016
15017   // this makes it possible to leave the removed element again
15018   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15019     Store[x][y] = element;
15020
15021   return TRUE;
15022 }
15023
15024 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
15025 {
15026   int jx = player->jx, jy = player->jy;
15027   int x = jx + dx, y = jy + dy;
15028   int snap_direction = (dx == -1 ? MV_LEFT  :
15029                         dx == +1 ? MV_RIGHT :
15030                         dy == -1 ? MV_UP    :
15031                         dy == +1 ? MV_DOWN  : MV_NONE);
15032   boolean can_continue_snapping = (level.continuous_snapping &&
15033                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
15034
15035   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
15036     return FALSE;
15037
15038   if (!player->active || !IN_LEV_FIELD(x, y))
15039     return FALSE;
15040
15041   if (dx && dy)
15042     return FALSE;
15043
15044   if (!dx && !dy)
15045   {
15046     if (player->MovPos == 0)
15047       player->is_pushing = FALSE;
15048
15049     player->is_snapping = FALSE;
15050
15051     if (player->MovPos == 0)
15052     {
15053       player->is_moving = FALSE;
15054       player->is_digging = FALSE;
15055       player->is_collecting = FALSE;
15056     }
15057
15058     return FALSE;
15059   }
15060
15061   // prevent snapping with already pressed snap key when not allowed
15062   if (player->is_snapping && !can_continue_snapping)
15063     return FALSE;
15064
15065   player->MovDir = snap_direction;
15066
15067   if (player->MovPos == 0)
15068   {
15069     player->is_moving = FALSE;
15070     player->is_digging = FALSE;
15071     player->is_collecting = FALSE;
15072   }
15073
15074   player->is_dropping = FALSE;
15075   player->is_dropping_pressed = FALSE;
15076   player->drop_pressed_delay = 0;
15077
15078   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
15079     return FALSE;
15080
15081   player->is_snapping = TRUE;
15082   player->is_active = TRUE;
15083
15084   if (player->MovPos == 0)
15085   {
15086     player->is_moving = FALSE;
15087     player->is_digging = FALSE;
15088     player->is_collecting = FALSE;
15089   }
15090
15091   if (player->MovPos != 0)      // prevent graphic bugs in versions < 2.2.0
15092     TEST_DrawLevelField(player->last_jx, player->last_jy);
15093
15094   TEST_DrawLevelField(x, y);
15095
15096   return TRUE;
15097 }
15098
15099 static boolean DropElement(struct PlayerInfo *player)
15100 {
15101   int old_element, new_element;
15102   int dropx = player->jx, dropy = player->jy;
15103   int drop_direction = player->MovDir;
15104   int drop_side = drop_direction;
15105   int drop_element = get_next_dropped_element(player);
15106
15107   /* do not drop an element on top of another element; when holding drop key
15108      pressed without moving, dropped element must move away before the next
15109      element can be dropped (this is especially important if the next element
15110      is dynamite, which can be placed on background for historical reasons) */
15111   if (PLAYER_DROPPING(player, dropx, dropy) && Tile[dropx][dropy] != EL_EMPTY)
15112     return MP_ACTION;
15113
15114   if (IS_THROWABLE(drop_element))
15115   {
15116     dropx += GET_DX_FROM_DIR(drop_direction);
15117     dropy += GET_DY_FROM_DIR(drop_direction);
15118
15119     if (!IN_LEV_FIELD(dropx, dropy))
15120       return FALSE;
15121   }
15122
15123   old_element = Tile[dropx][dropy];     // old element at dropping position
15124   new_element = drop_element;           // default: no change when dropping
15125
15126   // check if player is active, not moving and ready to drop
15127   if (!player->active || player->MovPos || player->drop_delay > 0)
15128     return FALSE;
15129
15130   // check if player has anything that can be dropped
15131   if (new_element == EL_UNDEFINED)
15132     return FALSE;
15133
15134   // only set if player has anything that can be dropped
15135   player->is_dropping_pressed = TRUE;
15136
15137   // check if drop key was pressed long enough for EM style dynamite
15138   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15139     return FALSE;
15140
15141   // check if anything can be dropped at the current position
15142   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15143     return FALSE;
15144
15145   // collected custom elements can only be dropped on empty fields
15146   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15147     return FALSE;
15148
15149   if (old_element != EL_EMPTY)
15150     Back[dropx][dropy] = old_element;   // store old element on this field
15151
15152   ResetGfxAnimation(dropx, dropy);
15153   ResetRandomAnimationValue(dropx, dropy);
15154
15155   if (player->inventory_size > 0 ||
15156       player->inventory_infinite_element != EL_UNDEFINED)
15157   {
15158     if (player->inventory_size > 0)
15159     {
15160       player->inventory_size--;
15161
15162       DrawGameDoorValues();
15163
15164       if (new_element == EL_DYNAMITE)
15165         new_element = EL_DYNAMITE_ACTIVE;
15166       else if (new_element == EL_EM_DYNAMITE)
15167         new_element = EL_EM_DYNAMITE_ACTIVE;
15168       else if (new_element == EL_SP_DISK_RED)
15169         new_element = EL_SP_DISK_RED_ACTIVE;
15170     }
15171
15172     Tile[dropx][dropy] = new_element;
15173
15174     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15175       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15176                           el2img(Tile[dropx][dropy]), 0);
15177
15178     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15179
15180     // needed if previous element just changed to "empty" in the last frame
15181     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
15182
15183     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15184                                player->index_bit, drop_side);
15185     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15186                                         CE_PLAYER_DROPS_X,
15187                                         player->index_bit, drop_side);
15188
15189     TestIfElementTouchesCustomElement(dropx, dropy);
15190   }
15191   else          // player is dropping a dyna bomb
15192   {
15193     player->dynabombs_left--;
15194
15195     Tile[dropx][dropy] = new_element;
15196
15197     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15198       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15199                           el2img(Tile[dropx][dropy]), 0);
15200
15201     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15202   }
15203
15204   if (Tile[dropx][dropy] == new_element) // uninitialized unless CE change
15205     InitField_WithBug1(dropx, dropy, FALSE);
15206
15207   new_element = Tile[dropx][dropy];     // element might have changed
15208
15209   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
15210       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
15211   {
15212     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
15213       MovDir[dropx][dropy] = drop_direction;
15214
15215     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
15216
15217     // do not cause impact style collision by dropping elements that can fall
15218     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15219   }
15220
15221   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
15222   player->is_dropping = TRUE;
15223
15224   player->drop_pressed_delay = 0;
15225   player->is_dropping_pressed = FALSE;
15226
15227   player->drop_x = dropx;
15228   player->drop_y = dropy;
15229
15230   return TRUE;
15231 }
15232
15233 // ----------------------------------------------------------------------------
15234 // game sound playing functions
15235 // ----------------------------------------------------------------------------
15236
15237 static int *loop_sound_frame = NULL;
15238 static int *loop_sound_volume = NULL;
15239
15240 void InitPlayLevelSound(void)
15241 {
15242   int num_sounds = getSoundListSize();
15243
15244   checked_free(loop_sound_frame);
15245   checked_free(loop_sound_volume);
15246
15247   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
15248   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
15249 }
15250
15251 static void PlayLevelSound(int x, int y, int nr)
15252 {
15253   int sx = SCREENX(x), sy = SCREENY(y);
15254   int volume, stereo_position;
15255   int max_distance = 8;
15256   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
15257
15258   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
15259       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
15260     return;
15261
15262   if (!IN_LEV_FIELD(x, y) ||
15263       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
15264       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
15265     return;
15266
15267   volume = SOUND_MAX_VOLUME;
15268
15269   if (!IN_SCR_FIELD(sx, sy))
15270   {
15271     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
15272     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
15273
15274     volume -= volume * (dx > dy ? dx : dy) / max_distance;
15275   }
15276
15277   stereo_position = (SOUND_MAX_LEFT +
15278                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
15279                      (SCR_FIELDX + 2 * max_distance));
15280
15281   if (IS_LOOP_SOUND(nr))
15282   {
15283     /* This assures that quieter loop sounds do not overwrite louder ones,
15284        while restarting sound volume comparison with each new game frame. */
15285
15286     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
15287       return;
15288
15289     loop_sound_volume[nr] = volume;
15290     loop_sound_frame[nr] = FrameCounter;
15291   }
15292
15293   PlaySoundExt(nr, volume, stereo_position, type);
15294 }
15295
15296 static void PlayLevelSoundNearest(int x, int y, int sound_action)
15297 {
15298   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
15299                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
15300                  y < LEVELY(BY1) ? LEVELY(BY1) :
15301                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
15302                  sound_action);
15303 }
15304
15305 static void PlayLevelSoundAction(int x, int y, int action)
15306 {
15307   PlayLevelSoundElementAction(x, y, Tile[x][y], action);
15308 }
15309
15310 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
15311 {
15312   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15313
15314   if (sound_effect != SND_UNDEFINED)
15315     PlayLevelSound(x, y, sound_effect);
15316 }
15317
15318 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
15319                                               int action)
15320 {
15321   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15322
15323   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15324     PlayLevelSound(x, y, sound_effect);
15325 }
15326
15327 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
15328 {
15329   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15330
15331   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15332     PlayLevelSound(x, y, sound_effect);
15333 }
15334
15335 static void StopLevelSoundActionIfLoop(int x, int y, int action)
15336 {
15337   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15338
15339   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15340     StopSound(sound_effect);
15341 }
15342
15343 static int getLevelMusicNr(void)
15344 {
15345   int level_pos = level_nr - leveldir_current->first_level;
15346
15347   if (levelset.music[level_nr] != MUS_UNDEFINED)
15348     return levelset.music[level_nr];            // from config file
15349   else
15350     return MAP_NOCONF_MUSIC(level_pos);         // from music dir
15351 }
15352
15353 static void FadeLevelSounds(void)
15354 {
15355   FadeSounds();
15356 }
15357
15358 static void FadeLevelMusic(void)
15359 {
15360   int music_nr = getLevelMusicNr();
15361   char *curr_music = getCurrentlyPlayingMusicFilename();
15362   char *next_music = getMusicInfoEntryFilename(music_nr);
15363
15364   if (!strEqual(curr_music, next_music))
15365     FadeMusic();
15366 }
15367
15368 void FadeLevelSoundsAndMusic(void)
15369 {
15370   FadeLevelSounds();
15371   FadeLevelMusic();
15372 }
15373
15374 static void PlayLevelMusic(void)
15375 {
15376   int music_nr = getLevelMusicNr();
15377   char *curr_music = getCurrentlyPlayingMusicFilename();
15378   char *next_music = getMusicInfoEntryFilename(music_nr);
15379
15380   if (!strEqual(curr_music, next_music))
15381     PlayMusicLoop(music_nr);
15382 }
15383
15384 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15385 {
15386   int element = (element_em > -1 ? map_element_EM_to_RND_game(element_em) : 0);
15387   int offset = 0;
15388   int x = xx - offset;
15389   int y = yy - offset;
15390
15391   switch (sample)
15392   {
15393     case SOUND_blank:
15394       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15395       break;
15396
15397     case SOUND_roll:
15398       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15399       break;
15400
15401     case SOUND_stone:
15402       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15403       break;
15404
15405     case SOUND_nut:
15406       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15407       break;
15408
15409     case SOUND_crack:
15410       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15411       break;
15412
15413     case SOUND_bug:
15414       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15415       break;
15416
15417     case SOUND_tank:
15418       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15419       break;
15420
15421     case SOUND_android_clone:
15422       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15423       break;
15424
15425     case SOUND_android_move:
15426       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15427       break;
15428
15429     case SOUND_spring:
15430       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15431       break;
15432
15433     case SOUND_slurp:
15434       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15435       break;
15436
15437     case SOUND_eater:
15438       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15439       break;
15440
15441     case SOUND_eater_eat:
15442       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15443       break;
15444
15445     case SOUND_alien:
15446       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15447       break;
15448
15449     case SOUND_collect:
15450       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15451       break;
15452
15453     case SOUND_diamond:
15454       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15455       break;
15456
15457     case SOUND_squash:
15458       // !!! CHECK THIS !!!
15459 #if 1
15460       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15461 #else
15462       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15463 #endif
15464       break;
15465
15466     case SOUND_wonderfall:
15467       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15468       break;
15469
15470     case SOUND_drip:
15471       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15472       break;
15473
15474     case SOUND_push:
15475       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15476       break;
15477
15478     case SOUND_dirt:
15479       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15480       break;
15481
15482     case SOUND_acid:
15483       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15484       break;
15485
15486     case SOUND_ball:
15487       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15488       break;
15489
15490     case SOUND_slide:
15491       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15492       break;
15493
15494     case SOUND_wonder:
15495       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15496       break;
15497
15498     case SOUND_door:
15499       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15500       break;
15501
15502     case SOUND_exit_open:
15503       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15504       break;
15505
15506     case SOUND_exit_leave:
15507       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15508       break;
15509
15510     case SOUND_dynamite:
15511       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15512       break;
15513
15514     case SOUND_tick:
15515       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15516       break;
15517
15518     case SOUND_press:
15519       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15520       break;
15521
15522     case SOUND_wheel:
15523       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15524       break;
15525
15526     case SOUND_boom:
15527       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15528       break;
15529
15530     case SOUND_die:
15531       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15532       break;
15533
15534     case SOUND_time:
15535       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15536       break;
15537
15538     default:
15539       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15540       break;
15541   }
15542 }
15543
15544 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
15545 {
15546   int element = map_element_SP_to_RND(element_sp);
15547   int action = map_action_SP_to_RND(action_sp);
15548   int offset = (setup.sp_show_border_elements ? 0 : 1);
15549   int x = xx - offset;
15550   int y = yy - offset;
15551
15552   PlayLevelSoundElementAction(x, y, element, action);
15553 }
15554
15555 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
15556 {
15557   int element = map_element_MM_to_RND(element_mm);
15558   int action = map_action_MM_to_RND(action_mm);
15559   int offset = 0;
15560   int x = xx - offset;
15561   int y = yy - offset;
15562
15563   if (!IS_MM_ELEMENT(element))
15564     element = EL_MM_DEFAULT;
15565
15566   PlayLevelSoundElementAction(x, y, element, action);
15567 }
15568
15569 void PlaySound_MM(int sound_mm)
15570 {
15571   int sound = map_sound_MM_to_RND(sound_mm);
15572
15573   if (sound == SND_UNDEFINED)
15574     return;
15575
15576   PlaySound(sound);
15577 }
15578
15579 void PlaySoundLoop_MM(int sound_mm)
15580 {
15581   int sound = map_sound_MM_to_RND(sound_mm);
15582
15583   if (sound == SND_UNDEFINED)
15584     return;
15585
15586   PlaySoundLoop(sound);
15587 }
15588
15589 void StopSound_MM(int sound_mm)
15590 {
15591   int sound = map_sound_MM_to_RND(sound_mm);
15592
15593   if (sound == SND_UNDEFINED)
15594     return;
15595
15596   StopSound(sound);
15597 }
15598
15599 void RaiseScore(int value)
15600 {
15601   game.score += value;
15602
15603   game_panel_controls[GAME_PANEL_SCORE].value = game.score;
15604
15605   DisplayGameControlValues();
15606 }
15607
15608 void RaiseScoreElement(int element)
15609 {
15610   switch (element)
15611   {
15612     case EL_EMERALD:
15613     case EL_BD_DIAMOND:
15614     case EL_EMERALD_YELLOW:
15615     case EL_EMERALD_RED:
15616     case EL_EMERALD_PURPLE:
15617     case EL_SP_INFOTRON:
15618       RaiseScore(level.score[SC_EMERALD]);
15619       break;
15620     case EL_DIAMOND:
15621       RaiseScore(level.score[SC_DIAMOND]);
15622       break;
15623     case EL_CRYSTAL:
15624       RaiseScore(level.score[SC_CRYSTAL]);
15625       break;
15626     case EL_PEARL:
15627       RaiseScore(level.score[SC_PEARL]);
15628       break;
15629     case EL_BUG:
15630     case EL_BD_BUTTERFLY:
15631     case EL_SP_ELECTRON:
15632       RaiseScore(level.score[SC_BUG]);
15633       break;
15634     case EL_SPACESHIP:
15635     case EL_BD_FIREFLY:
15636     case EL_SP_SNIKSNAK:
15637       RaiseScore(level.score[SC_SPACESHIP]);
15638       break;
15639     case EL_YAMYAM:
15640     case EL_DARK_YAMYAM:
15641       RaiseScore(level.score[SC_YAMYAM]);
15642       break;
15643     case EL_ROBOT:
15644       RaiseScore(level.score[SC_ROBOT]);
15645       break;
15646     case EL_PACMAN:
15647       RaiseScore(level.score[SC_PACMAN]);
15648       break;
15649     case EL_NUT:
15650       RaiseScore(level.score[SC_NUT]);
15651       break;
15652     case EL_DYNAMITE:
15653     case EL_EM_DYNAMITE:
15654     case EL_SP_DISK_RED:
15655     case EL_DYNABOMB_INCREASE_NUMBER:
15656     case EL_DYNABOMB_INCREASE_SIZE:
15657     case EL_DYNABOMB_INCREASE_POWER:
15658       RaiseScore(level.score[SC_DYNAMITE]);
15659       break;
15660     case EL_SHIELD_NORMAL:
15661     case EL_SHIELD_DEADLY:
15662       RaiseScore(level.score[SC_SHIELD]);
15663       break;
15664     case EL_EXTRA_TIME:
15665       RaiseScore(level.extra_time_score);
15666       break;
15667     case EL_KEY_1:
15668     case EL_KEY_2:
15669     case EL_KEY_3:
15670     case EL_KEY_4:
15671     case EL_EM_KEY_1:
15672     case EL_EM_KEY_2:
15673     case EL_EM_KEY_3:
15674     case EL_EM_KEY_4:
15675     case EL_EMC_KEY_5:
15676     case EL_EMC_KEY_6:
15677     case EL_EMC_KEY_7:
15678     case EL_EMC_KEY_8:
15679     case EL_DC_KEY_WHITE:
15680       RaiseScore(level.score[SC_KEY]);
15681       break;
15682     default:
15683       RaiseScore(element_info[element].collect_score);
15684       break;
15685   }
15686 }
15687
15688 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15689 {
15690   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15691   {
15692     if (!quick_quit)
15693     {
15694       // prevent short reactivation of overlay buttons while closing door
15695       SetOverlayActive(FALSE);
15696       UnmapGameButtons();
15697
15698       // door may still be open due to skipped or envelope style request
15699       CloseDoor(score_info_tape_play ? DOOR_CLOSE_ALL : DOOR_CLOSE_1);
15700     }
15701
15702     if (network.enabled)
15703       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15704     else
15705     {
15706       if (quick_quit)
15707         FadeSkipNextFadeIn();
15708
15709       SetGameStatus(GAME_MODE_MAIN);
15710
15711       DrawMainMenu();
15712     }
15713   }
15714   else          // continue playing the game
15715   {
15716     if (tape.playing && tape.deactivate_display)
15717       TapeDeactivateDisplayOff(TRUE);
15718
15719     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15720
15721     if (tape.playing && tape.deactivate_display)
15722       TapeDeactivateDisplayOn();
15723   }
15724 }
15725
15726 void RequestQuitGame(boolean escape_key_pressed)
15727 {
15728   boolean ask_on_escape = (setup.ask_on_escape && setup.ask_on_quit_game);
15729   boolean quick_quit = ((escape_key_pressed && !ask_on_escape) ||
15730                         level_editor_test_game);
15731   boolean skip_request = (game.all_players_gone || !setup.ask_on_quit_game ||
15732                           quick_quit || score_info_tape_play);
15733
15734   RequestQuitGameExt(skip_request, quick_quit,
15735                      "Do you really want to quit the game?");
15736 }
15737
15738 static char *getRestartGameMessage(void)
15739 {
15740   boolean play_again = hasStartedNetworkGame();
15741   static char message[MAX_OUTPUT_LINESIZE];
15742   char *game_over_text = "Game over!";
15743   char *play_again_text = " Play it again?";
15744
15745   if (level.game_engine_type == GAME_ENGINE_TYPE_MM &&
15746       game_mm.game_over_message != NULL)
15747     game_over_text = game_mm.game_over_message;
15748
15749   snprintf(message, MAX_OUTPUT_LINESIZE, "%s%s", game_over_text,
15750            (play_again ? play_again_text : ""));
15751
15752   return message;
15753 }
15754
15755 static void RequestRestartGame(void)
15756 {
15757   char *message = getRestartGameMessage();
15758   boolean has_started_game = hasStartedNetworkGame();
15759   int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM);
15760   int door_state = DOOR_CLOSE_1;
15761
15762   if (Request(message, request_mode | REQ_STAY_OPEN) && has_started_game)
15763   {
15764     CloseDoor(door_state);
15765
15766     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15767   }
15768   else
15769   {
15770     // if game was invoked from level editor, also close tape recorder door
15771     if (level_editor_test_game)
15772       door_state = DOOR_CLOSE_ALL;
15773
15774     CloseDoor(door_state);
15775
15776     SetGameStatus(GAME_MODE_MAIN);
15777
15778     DrawMainMenu();
15779   }
15780 }
15781
15782 boolean CheckRestartGame(void)
15783 {
15784   static int game_over_delay = 0;
15785   int game_over_delay_value = 50;
15786   boolean game_over = checkGameFailed();
15787
15788   if (!game_over)
15789   {
15790     game_over_delay = game_over_delay_value;
15791
15792     return FALSE;
15793   }
15794
15795   if (game_over_delay > 0)
15796   {
15797     if (game_over_delay == game_over_delay_value / 2)
15798       PlaySound(SND_GAME_LOSING);
15799
15800     game_over_delay--;
15801
15802     return FALSE;
15803   }
15804
15805   // do not ask to play again if request dialog is already active
15806   if (game.request_active)
15807     return FALSE;
15808
15809   // do not ask to play again if game was never actually played
15810   if (!game.GamePlayed)
15811     return FALSE;
15812
15813   // do not ask to play again if this was disabled in setup menu
15814   if (!setup.ask_on_game_over)
15815     return FALSE;
15816
15817   RequestRestartGame();
15818
15819   return TRUE;
15820 }
15821
15822 boolean checkGameSolved(void)
15823 {
15824   // set for all game engines if level was solved
15825   return game.LevelSolved_GameEnd;
15826 }
15827
15828 boolean checkGameFailed(void)
15829 {
15830   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15831     return (game_em.game_over && !game_em.level_solved);
15832   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15833     return (game_sp.game_over && !game_sp.level_solved);
15834   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15835     return (game_mm.game_over && !game_mm.level_solved);
15836   else                          // GAME_ENGINE_TYPE_RND
15837     return (game.GameOver && !game.LevelSolved);
15838 }
15839
15840 boolean checkGameEnded(void)
15841 {
15842   return (checkGameSolved() || checkGameFailed());
15843 }
15844
15845
15846 // ----------------------------------------------------------------------------
15847 // random generator functions
15848 // ----------------------------------------------------------------------------
15849
15850 unsigned int InitEngineRandom_RND(int seed)
15851 {
15852   game.num_random_calls = 0;
15853
15854   return InitEngineRandom(seed);
15855 }
15856
15857 unsigned int RND(int max)
15858 {
15859   if (max > 0)
15860   {
15861     game.num_random_calls++;
15862
15863     return GetEngineRandom(max);
15864   }
15865
15866   return 0;
15867 }
15868
15869
15870 // ----------------------------------------------------------------------------
15871 // game engine snapshot handling functions
15872 // ----------------------------------------------------------------------------
15873
15874 struct EngineSnapshotInfo
15875 {
15876   // runtime values for custom element collect score
15877   int collect_score[NUM_CUSTOM_ELEMENTS];
15878
15879   // runtime values for group element choice position
15880   int choice_pos[NUM_GROUP_ELEMENTS];
15881
15882   // runtime values for belt position animations
15883   int belt_graphic[4][NUM_BELT_PARTS];
15884   int belt_anim_mode[4][NUM_BELT_PARTS];
15885 };
15886
15887 static struct EngineSnapshotInfo engine_snapshot_rnd;
15888 static char *snapshot_level_identifier = NULL;
15889 static int snapshot_level_nr = -1;
15890
15891 static void SaveEngineSnapshotValues_RND(void)
15892 {
15893   static int belt_base_active_element[4] =
15894   {
15895     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15896     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15897     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15898     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15899   };
15900   int i, j;
15901
15902   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15903   {
15904     int element = EL_CUSTOM_START + i;
15905
15906     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15907   }
15908
15909   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15910   {
15911     int element = EL_GROUP_START + i;
15912
15913     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15914   }
15915
15916   for (i = 0; i < 4; i++)
15917   {
15918     for (j = 0; j < NUM_BELT_PARTS; j++)
15919     {
15920       int element = belt_base_active_element[i] + j;
15921       int graphic = el2img(element);
15922       int anim_mode = graphic_info[graphic].anim_mode;
15923
15924       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15925       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15926     }
15927   }
15928 }
15929
15930 static void LoadEngineSnapshotValues_RND(void)
15931 {
15932   unsigned int num_random_calls = game.num_random_calls;
15933   int i, j;
15934
15935   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15936   {
15937     int element = EL_CUSTOM_START + i;
15938
15939     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15940   }
15941
15942   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15943   {
15944     int element = EL_GROUP_START + i;
15945
15946     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15947   }
15948
15949   for (i = 0; i < 4; i++)
15950   {
15951     for (j = 0; j < NUM_BELT_PARTS; j++)
15952     {
15953       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15954       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15955
15956       graphic_info[graphic].anim_mode = anim_mode;
15957     }
15958   }
15959
15960   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15961   {
15962     InitRND(tape.random_seed);
15963     for (i = 0; i < num_random_calls; i++)
15964       RND(1);
15965   }
15966
15967   if (game.num_random_calls != num_random_calls)
15968   {
15969     Error("number of random calls out of sync");
15970     Error("number of random calls should be %d", num_random_calls);
15971     Error("number of random calls is %d", game.num_random_calls);
15972
15973     Fail("this should not happen -- please debug");
15974   }
15975 }
15976
15977 void FreeEngineSnapshotSingle(void)
15978 {
15979   FreeSnapshotSingle();
15980
15981   setString(&snapshot_level_identifier, NULL);
15982   snapshot_level_nr = -1;
15983 }
15984
15985 void FreeEngineSnapshotList(void)
15986 {
15987   FreeSnapshotList();
15988 }
15989
15990 static ListNode *SaveEngineSnapshotBuffers(void)
15991 {
15992   ListNode *buffers = NULL;
15993
15994   // copy some special values to a structure better suited for the snapshot
15995
15996   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15997     SaveEngineSnapshotValues_RND();
15998   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15999     SaveEngineSnapshotValues_EM();
16000   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
16001     SaveEngineSnapshotValues_SP(&buffers);
16002   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
16003     SaveEngineSnapshotValues_MM();
16004
16005   // save values stored in special snapshot structure
16006
16007   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16008     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
16009   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
16010     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
16011   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
16012     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
16013   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
16014     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
16015
16016   // save further RND engine values
16017
16018   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
16019   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
16020   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
16021
16022   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
16023   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
16024   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
16025   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
16026   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
16027
16028   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
16029   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
16030   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
16031
16032   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
16033
16034   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
16035   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
16036
16037   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Tile));
16038   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
16039   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
16040   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
16041   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
16042   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
16043   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
16044   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
16045   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
16046   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
16047   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
16048   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
16049   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
16050   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
16051   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
16052   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
16053   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
16054   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
16055
16056   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
16057   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
16058
16059   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
16060   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
16061   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
16062
16063   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
16064   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
16065
16066   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
16067   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
16068   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandomStatic));
16069   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
16070   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
16071   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
16072
16073   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
16074   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
16075
16076 #if 0
16077   ListNode *node = engine_snapshot_list_rnd;
16078   int num_bytes = 0;
16079
16080   while (node != NULL)
16081   {
16082     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
16083
16084     node = node->next;
16085   }
16086
16087   Debug("game:playing:SaveEngineSnapshotBuffers",
16088         "size of engine snapshot: %d bytes", num_bytes);
16089 #endif
16090
16091   return buffers;
16092 }
16093
16094 void SaveEngineSnapshotSingle(void)
16095 {
16096   ListNode *buffers = SaveEngineSnapshotBuffers();
16097
16098   // finally save all snapshot buffers to single snapshot
16099   SaveSnapshotSingle(buffers);
16100
16101   // save level identification information
16102   setString(&snapshot_level_identifier, leveldir_current->identifier);
16103   snapshot_level_nr = level_nr;
16104 }
16105
16106 boolean CheckSaveEngineSnapshotToList(void)
16107 {
16108   boolean save_snapshot =
16109     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
16110      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
16111       game.snapshot.changed_action) ||
16112      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16113       game.snapshot.collected_item));
16114
16115   game.snapshot.changed_action = FALSE;
16116   game.snapshot.collected_item = FALSE;
16117   game.snapshot.save_snapshot = save_snapshot;
16118
16119   return save_snapshot;
16120 }
16121
16122 void SaveEngineSnapshotToList(void)
16123 {
16124   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
16125       tape.quick_resume)
16126     return;
16127
16128   ListNode *buffers = SaveEngineSnapshotBuffers();
16129
16130   // finally save all snapshot buffers to snapshot list
16131   SaveSnapshotToList(buffers);
16132 }
16133
16134 void SaveEngineSnapshotToListInitial(void)
16135 {
16136   FreeEngineSnapshotList();
16137
16138   SaveEngineSnapshotToList();
16139 }
16140
16141 static void LoadEngineSnapshotValues(void)
16142 {
16143   // restore special values from snapshot structure
16144
16145   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16146     LoadEngineSnapshotValues_RND();
16147   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
16148     LoadEngineSnapshotValues_EM();
16149   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
16150     LoadEngineSnapshotValues_SP();
16151   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
16152     LoadEngineSnapshotValues_MM();
16153 }
16154
16155 void LoadEngineSnapshotSingle(void)
16156 {
16157   LoadSnapshotSingle();
16158
16159   LoadEngineSnapshotValues();
16160 }
16161
16162 static void LoadEngineSnapshot_Undo(int steps)
16163 {
16164   LoadSnapshotFromList_Older(steps);
16165
16166   LoadEngineSnapshotValues();
16167 }
16168
16169 static void LoadEngineSnapshot_Redo(int steps)
16170 {
16171   LoadSnapshotFromList_Newer(steps);
16172
16173   LoadEngineSnapshotValues();
16174 }
16175
16176 boolean CheckEngineSnapshotSingle(void)
16177 {
16178   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16179           snapshot_level_nr == level_nr);
16180 }
16181
16182 boolean CheckEngineSnapshotList(void)
16183 {
16184   return CheckSnapshotList();
16185 }
16186
16187
16188 // ---------- new game button stuff -------------------------------------------
16189
16190 static struct
16191 {
16192   int graphic;
16193   struct XY *pos;
16194   int gadget_id;
16195   boolean *setup_value;
16196   boolean allowed_on_tape;
16197   boolean is_touch_button;
16198   char *infotext;
16199 } gamebutton_info[NUM_GAME_BUTTONS] =
16200 {
16201   {
16202     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
16203     GAME_CTRL_ID_STOP,                          NULL,
16204     TRUE, FALSE,                                "stop game"
16205   },
16206   {
16207     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
16208     GAME_CTRL_ID_PAUSE,                         NULL,
16209     TRUE, FALSE,                                "pause game"
16210   },
16211   {
16212     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
16213     GAME_CTRL_ID_PLAY,                          NULL,
16214     TRUE, FALSE,                                "play game"
16215   },
16216   {
16217     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
16218     GAME_CTRL_ID_UNDO,                          NULL,
16219     TRUE, FALSE,                                "undo step"
16220   },
16221   {
16222     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
16223     GAME_CTRL_ID_REDO,                          NULL,
16224     TRUE, FALSE,                                "redo step"
16225   },
16226   {
16227     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
16228     GAME_CTRL_ID_SAVE,                          NULL,
16229     TRUE, FALSE,                                "save game"
16230   },
16231   {
16232     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
16233     GAME_CTRL_ID_PAUSE2,                        NULL,
16234     TRUE, FALSE,                                "pause game"
16235   },
16236   {
16237     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
16238     GAME_CTRL_ID_LOAD,                          NULL,
16239     TRUE, FALSE,                                "load game"
16240   },
16241   {
16242     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
16243     GAME_CTRL_ID_PANEL_STOP,                    NULL,
16244     FALSE, FALSE,                               "stop game"
16245   },
16246   {
16247     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
16248     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
16249     FALSE, FALSE,                               "pause game"
16250   },
16251   {
16252     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
16253     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
16254     FALSE, FALSE,                               "play game"
16255   },
16256   {
16257     IMG_GFX_GAME_BUTTON_TOUCH_STOP,             &game.button.touch_stop,
16258     GAME_CTRL_ID_TOUCH_STOP,                    NULL,
16259     FALSE, TRUE,                                "stop game"
16260   },
16261   {
16262     IMG_GFX_GAME_BUTTON_TOUCH_PAUSE,            &game.button.touch_pause,
16263     GAME_CTRL_ID_TOUCH_PAUSE,                   NULL,
16264     FALSE, TRUE,                                "pause game"
16265   },
16266   {
16267     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
16268     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
16269     TRUE, FALSE,                                "background music on/off"
16270   },
16271   {
16272     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
16273     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
16274     TRUE, FALSE,                                "sound loops on/off"
16275   },
16276   {
16277     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
16278     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
16279     TRUE, FALSE,                                "normal sounds on/off"
16280   },
16281   {
16282     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
16283     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
16284     FALSE, FALSE,                               "background music on/off"
16285   },
16286   {
16287     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
16288     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
16289     FALSE, FALSE,                               "sound loops on/off"
16290   },
16291   {
16292     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
16293     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
16294     FALSE, FALSE,                               "normal sounds on/off"
16295   }
16296 };
16297
16298 void CreateGameButtons(void)
16299 {
16300   int i;
16301
16302   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16303   {
16304     int graphic = gamebutton_info[i].graphic;
16305     struct GraphicInfo *gfx = &graphic_info[graphic];
16306     struct XY *pos = gamebutton_info[i].pos;
16307     struct GadgetInfo *gi;
16308     int button_type;
16309     boolean checked;
16310     unsigned int event_mask;
16311     boolean is_touch_button = gamebutton_info[i].is_touch_button;
16312     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
16313     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
16314     int base_x = (is_touch_button ? 0 : on_tape ? VX : DX);
16315     int base_y = (is_touch_button ? 0 : on_tape ? VY : DY);
16316     int gd_x   = gfx->src_x;
16317     int gd_y   = gfx->src_y;
16318     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
16319     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
16320     int gd_xa  = gfx->src_x + gfx->active_xoffset;
16321     int gd_ya  = gfx->src_y + gfx->active_yoffset;
16322     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
16323     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
16324     int x = (is_touch_button ? pos->x : GDI_ACTIVE_POS(pos->x));
16325     int y = (is_touch_button ? pos->y : GDI_ACTIVE_POS(pos->y));
16326     int id = i;
16327
16328     // do not use touch buttons if overlay touch buttons are disabled
16329     if (is_touch_button && !setup.touch.overlay_buttons)
16330       continue;
16331
16332     if (gfx->bitmap == NULL)
16333     {
16334       game_gadget[id] = NULL;
16335
16336       continue;
16337     }
16338
16339     if (id == GAME_CTRL_ID_STOP ||
16340         id == GAME_CTRL_ID_PANEL_STOP ||
16341         id == GAME_CTRL_ID_TOUCH_STOP ||
16342         id == GAME_CTRL_ID_PLAY ||
16343         id == GAME_CTRL_ID_PANEL_PLAY ||
16344         id == GAME_CTRL_ID_SAVE ||
16345         id == GAME_CTRL_ID_LOAD)
16346     {
16347       button_type = GD_TYPE_NORMAL_BUTTON;
16348       checked = FALSE;
16349       event_mask = GD_EVENT_RELEASED;
16350     }
16351     else if (id == GAME_CTRL_ID_UNDO ||
16352              id == GAME_CTRL_ID_REDO)
16353     {
16354       button_type = GD_TYPE_NORMAL_BUTTON;
16355       checked = FALSE;
16356       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
16357     }
16358     else
16359     {
16360       button_type = GD_TYPE_CHECK_BUTTON;
16361       checked = (gamebutton_info[i].setup_value != NULL ?
16362                  *gamebutton_info[i].setup_value : FALSE);
16363       event_mask = GD_EVENT_PRESSED;
16364     }
16365
16366     gi = CreateGadget(GDI_CUSTOM_ID, id,
16367                       GDI_IMAGE_ID, graphic,
16368                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
16369                       GDI_X, base_x + x,
16370                       GDI_Y, base_y + y,
16371                       GDI_WIDTH, gfx->width,
16372                       GDI_HEIGHT, gfx->height,
16373                       GDI_TYPE, button_type,
16374                       GDI_STATE, GD_BUTTON_UNPRESSED,
16375                       GDI_CHECKED, checked,
16376                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
16377                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
16378                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
16379                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
16380                       GDI_DIRECT_DRAW, FALSE,
16381                       GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
16382                       GDI_EVENT_MASK, event_mask,
16383                       GDI_CALLBACK_ACTION, HandleGameButtons,
16384                       GDI_END);
16385
16386     if (gi == NULL)
16387       Fail("cannot create gadget");
16388
16389     game_gadget[id] = gi;
16390   }
16391 }
16392
16393 void FreeGameButtons(void)
16394 {
16395   int i;
16396
16397   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16398     FreeGadget(game_gadget[i]);
16399 }
16400
16401 static void UnmapGameButtonsAtSamePosition(int id)
16402 {
16403   int i;
16404
16405   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16406     if (i != id &&
16407         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
16408         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
16409       UnmapGadget(game_gadget[i]);
16410 }
16411
16412 static void UnmapGameButtonsAtSamePosition_All(void)
16413 {
16414   if (setup.show_load_save_buttons)
16415   {
16416     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16417     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16418     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16419   }
16420   else if (setup.show_undo_redo_buttons)
16421   {
16422     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16423     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16424     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16425   }
16426   else
16427   {
16428     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
16429     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
16430     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
16431
16432     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
16433     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
16434     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
16435   }
16436 }
16437
16438 void MapLoadSaveButtons(void)
16439 {
16440   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16441   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16442
16443   MapGadget(game_gadget[GAME_CTRL_ID_LOAD]);
16444   MapGadget(game_gadget[GAME_CTRL_ID_SAVE]);
16445 }
16446
16447 void MapUndoRedoButtons(void)
16448 {
16449   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16450   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16451
16452   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
16453   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
16454 }
16455
16456 void ModifyPauseButtons(void)
16457 {
16458   static int ids[] =
16459   {
16460     GAME_CTRL_ID_PAUSE,
16461     GAME_CTRL_ID_PAUSE2,
16462     GAME_CTRL_ID_PANEL_PAUSE,
16463     GAME_CTRL_ID_TOUCH_PAUSE,
16464     -1
16465   };
16466   int i;
16467
16468   for (i = 0; ids[i] > -1; i++)
16469     ModifyGadget(game_gadget[ids[i]], GDI_CHECKED, tape.pausing, GDI_END);
16470 }
16471
16472 static void MapGameButtonsExt(boolean on_tape)
16473 {
16474   int i;
16475
16476   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16477   {
16478     if ((i == GAME_CTRL_ID_UNDO ||
16479          i == GAME_CTRL_ID_REDO) &&
16480         game_status != GAME_MODE_PLAYING)
16481       continue;
16482
16483     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16484       MapGadget(game_gadget[i]);
16485   }
16486
16487   UnmapGameButtonsAtSamePosition_All();
16488
16489   RedrawGameButtons();
16490 }
16491
16492 static void UnmapGameButtonsExt(boolean on_tape)
16493 {
16494   int i;
16495
16496   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16497     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16498       UnmapGadget(game_gadget[i]);
16499 }
16500
16501 static void RedrawGameButtonsExt(boolean on_tape)
16502 {
16503   int i;
16504
16505   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16506     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16507       RedrawGadget(game_gadget[i]);
16508 }
16509
16510 static void SetGadgetState(struct GadgetInfo *gi, boolean state)
16511 {
16512   if (gi == NULL)
16513     return;
16514
16515   gi->checked = state;
16516 }
16517
16518 static void RedrawSoundButtonGadget(int id)
16519 {
16520   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
16521              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
16522              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
16523              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
16524              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
16525              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
16526              id);
16527
16528   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
16529   RedrawGadget(game_gadget[id2]);
16530 }
16531
16532 void MapGameButtons(void)
16533 {
16534   MapGameButtonsExt(FALSE);
16535 }
16536
16537 void UnmapGameButtons(void)
16538 {
16539   UnmapGameButtonsExt(FALSE);
16540 }
16541
16542 void RedrawGameButtons(void)
16543 {
16544   RedrawGameButtonsExt(FALSE);
16545 }
16546
16547 void MapGameButtonsOnTape(void)
16548 {
16549   MapGameButtonsExt(TRUE);
16550 }
16551
16552 void UnmapGameButtonsOnTape(void)
16553 {
16554   UnmapGameButtonsExt(TRUE);
16555 }
16556
16557 void RedrawGameButtonsOnTape(void)
16558 {
16559   RedrawGameButtonsExt(TRUE);
16560 }
16561
16562 static void GameUndoRedoExt(void)
16563 {
16564   ClearPlayerAction();
16565
16566   tape.pausing = TRUE;
16567
16568   RedrawPlayfield();
16569   UpdateAndDisplayGameControlValues();
16570
16571   DrawCompleteVideoDisplay();
16572   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
16573   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
16574   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
16575
16576   ModifyPauseButtons();
16577
16578   BackToFront();
16579 }
16580
16581 static void GameUndo(int steps)
16582 {
16583   if (!CheckEngineSnapshotList())
16584     return;
16585
16586   int tape_property_bits = tape.property_bits;
16587
16588   LoadEngineSnapshot_Undo(steps);
16589
16590   tape.property_bits |= tape_property_bits | TAPE_PROPERTY_SNAPSHOT;
16591
16592   GameUndoRedoExt();
16593 }
16594
16595 static void GameRedo(int steps)
16596 {
16597   if (!CheckEngineSnapshotList())
16598     return;
16599
16600   int tape_property_bits = tape.property_bits;
16601
16602   LoadEngineSnapshot_Redo(steps);
16603
16604   tape.property_bits |= tape_property_bits | TAPE_PROPERTY_SNAPSHOT;
16605
16606   GameUndoRedoExt();
16607 }
16608
16609 static void HandleGameButtonsExt(int id, int button)
16610 {
16611   static boolean game_undo_executed = FALSE;
16612   int steps = BUTTON_STEPSIZE(button);
16613   boolean handle_game_buttons =
16614     (game_status == GAME_MODE_PLAYING ||
16615      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
16616
16617   if (!handle_game_buttons)
16618     return;
16619
16620   switch (id)
16621   {
16622     case GAME_CTRL_ID_STOP:
16623     case GAME_CTRL_ID_PANEL_STOP:
16624     case GAME_CTRL_ID_TOUCH_STOP:
16625       TapeStopGame();
16626
16627       break;
16628
16629     case GAME_CTRL_ID_PAUSE:
16630     case GAME_CTRL_ID_PAUSE2:
16631     case GAME_CTRL_ID_PANEL_PAUSE:
16632     case GAME_CTRL_ID_TOUCH_PAUSE:
16633       if (network.enabled && game_status == GAME_MODE_PLAYING)
16634       {
16635         if (tape.pausing)
16636           SendToServer_ContinuePlaying();
16637         else
16638           SendToServer_PausePlaying();
16639       }
16640       else
16641         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16642
16643       game_undo_executed = FALSE;
16644
16645       break;
16646
16647     case GAME_CTRL_ID_PLAY:
16648     case GAME_CTRL_ID_PANEL_PLAY:
16649       if (game_status == GAME_MODE_MAIN)
16650       {
16651         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
16652       }
16653       else if (tape.pausing)
16654       {
16655         if (network.enabled)
16656           SendToServer_ContinuePlaying();
16657         else
16658           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
16659       }
16660       break;
16661
16662     case GAME_CTRL_ID_UNDO:
16663       // Important: When using "save snapshot when collecting an item" mode,
16664       // load last (current) snapshot for first "undo" after pressing "pause"
16665       // (else the last-but-one snapshot would be loaded, because the snapshot
16666       // pointer already points to the last snapshot when pressing "pause",
16667       // which is fine for "every step/move" mode, but not for "every collect")
16668       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16669           !game_undo_executed)
16670         steps--;
16671
16672       game_undo_executed = TRUE;
16673
16674       GameUndo(steps);
16675       break;
16676
16677     case GAME_CTRL_ID_REDO:
16678       GameRedo(steps);
16679       break;
16680
16681     case GAME_CTRL_ID_SAVE:
16682       TapeQuickSave();
16683       break;
16684
16685     case GAME_CTRL_ID_LOAD:
16686       TapeQuickLoad();
16687       break;
16688
16689     case SOUND_CTRL_ID_MUSIC:
16690     case SOUND_CTRL_ID_PANEL_MUSIC:
16691       if (setup.sound_music)
16692       { 
16693         setup.sound_music = FALSE;
16694
16695         FadeMusic();
16696       }
16697       else if (audio.music_available)
16698       { 
16699         setup.sound = setup.sound_music = TRUE;
16700
16701         SetAudioMode(setup.sound);
16702
16703         if (game_status == GAME_MODE_PLAYING)
16704           PlayLevelMusic();
16705       }
16706
16707       RedrawSoundButtonGadget(id);
16708
16709       break;
16710
16711     case SOUND_CTRL_ID_LOOPS:
16712     case SOUND_CTRL_ID_PANEL_LOOPS:
16713       if (setup.sound_loops)
16714         setup.sound_loops = FALSE;
16715       else if (audio.loops_available)
16716       {
16717         setup.sound = setup.sound_loops = TRUE;
16718
16719         SetAudioMode(setup.sound);
16720       }
16721
16722       RedrawSoundButtonGadget(id);
16723
16724       break;
16725
16726     case SOUND_CTRL_ID_SIMPLE:
16727     case SOUND_CTRL_ID_PANEL_SIMPLE:
16728       if (setup.sound_simple)
16729         setup.sound_simple = FALSE;
16730       else if (audio.sound_available)
16731       {
16732         setup.sound = setup.sound_simple = TRUE;
16733
16734         SetAudioMode(setup.sound);
16735       }
16736
16737       RedrawSoundButtonGadget(id);
16738
16739       break;
16740
16741     default:
16742       break;
16743   }
16744 }
16745
16746 static void HandleGameButtons(struct GadgetInfo *gi)
16747 {
16748   HandleGameButtonsExt(gi->custom_id, gi->event.button);
16749 }
16750
16751 void HandleSoundButtonKeys(Key key)
16752 {
16753   if (key == setup.shortcut.sound_simple)
16754     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
16755   else if (key == setup.shortcut.sound_loops)
16756     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
16757   else if (key == setup.shortcut.sound_music)
16758     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
16759 }